@tensorfeed/mcp-server 1.24.0 → 1.25.1

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 CHANGED
@@ -2,6 +2,16 @@
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.25.1 - 2026-05-07
6
+
7
+ ### Fixed
8
+ - `serverInfo.version` reported over the MCP `initialize` response was hardcoded to `1.10.0` and went stale through several releases. Version now reads from `package.json` at startup, so it can never drift again. Same constant feeds the outgoing `User-Agent` header on TensorFeed API calls. No behavior change for tools or transport.
9
+
10
+ ## 1.25.0 - 2026-05-06
11
+
12
+ ### Added
13
+ - Tool annotations on every registered tool (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`). All `get_*`, `*_series`, `*_search`, `compare_*`, `provider_deepdive`, `cost_projection`, `whats_new`, `*_snapshot`, `probe_*`, `list_watches`, `premium_routing`, and `premium_agents_directory` tools are flagged read-only + idempotent + open-world. The four `create_*_watch` tools are flagged write + non-idempotent. `delete_watch` is flagged destructive + idempotent. Hosts and clients can now decide whether a tool is safe to batch, retry, or call autonomously without re-prompting the user. Required by the Anthropic Connectors Directory.
14
+
5
15
  ## 1.24.0 - 2026-05-06
6
16
 
7
17
  ### Added
package/dist/index.js CHANGED
@@ -2,9 +2,13 @@
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { z } from 'zod';
5
+ import { readFileSync } from 'node:fs';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { dirname, join } from 'node:path';
5
8
  import { sanitizeToolResponse } from './sanitize.js';
6
9
  const API_BASE = 'https://tensorfeed.ai/api';
7
- const SDK_VERSION = '1.10.0';
10
+ const pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
11
+ const SDK_VERSION = JSON.parse(readFileSync(pkgPath, 'utf-8')).version;
8
12
  async function fetchJSON(path, opts = {}) {
9
13
  const headers = {
10
14
  'User-Agent': `TensorFeed-MCP/${SDK_VERSION}`,
@@ -56,14 +60,44 @@ const server = new McpServer({
56
60
  // covers any new endpoint that might forget it, plus protects against
57
61
  // drift between worker rules and what the MCP server should consider
58
62
  // safe. See ./sanitize.ts.
59
- function registerTool(name, description, paramsSchema, cb) {
63
+ // MCP tool annotation presets. These are HINTS for hosts/clients and agents
64
+ // so they can decide whether a tool is safe to call autonomously, batch, or
65
+ // retry. Required by the Anthropic Connectors Directory and used by client
66
+ // tool-pickers. See https://modelcontextprotocol.io/specification#tool-annotations
67
+ //
68
+ // readOnlyHint: true if the tool only reads, no side effects on the world
69
+ // destructiveHint: true if the tool may delete/destroy data
70
+ // idempotentHint: true if calling twice has the same effect as once
71
+ // openWorldHint: true if the tool interacts with external systems (HTTP/APIs)
72
+ //
73
+ // All TensorFeed tools hit the tensorfeed.ai HTTP API, so openWorldHint is
74
+ // always true here.
75
+ const READ_TOOL = {
76
+ readOnlyHint: true,
77
+ destructiveHint: false,
78
+ idempotentHint: true,
79
+ openWorldHint: true,
80
+ };
81
+ const CREATE_TOOL = {
82
+ readOnlyHint: false,
83
+ destructiveHint: false,
84
+ idempotentHint: false,
85
+ openWorldHint: true,
86
+ };
87
+ const DELETE_TOOL = {
88
+ readOnlyHint: false,
89
+ destructiveHint: true,
90
+ idempotentHint: true,
91
+ openWorldHint: true,
92
+ };
93
+ function registerTool(name, description, paramsSchema, cb, annotations = READ_TOOL) {
60
94
  const wrapped = (async (args, extra) => {
61
95
  const result = await cb(args, extra);
62
96
  return sanitizeToolResponse(result);
63
97
  });
64
98
  // Direct dispatch to the SDK's underlying registration. Do not call
65
99
  // registerTool() here or it will recurse forever.
66
- return server.tool(name, description, paramsSchema, wrapped);
100
+ return server.tool(name, description, paramsSchema, annotations, wrapped);
67
101
  }
68
102
  // ── Tool: get_ai_news ───────────────────────────────────────────────
69
103
  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.', {
@@ -727,7 +761,7 @@ registerTool('create_price_watch', 'Register a webhook watch on a model price ch
727
761
  },
728
762
  ],
729
763
  };
730
- });
764
+ }, CREATE_TOOL);
731
765
  // ── Tool: create_status_watch (1 credit) ────────────────────────────
732
766
  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.', {
733
767
  provider: z.string().describe('Provider name (e.g. anthropic, openai)'),
@@ -751,7 +785,7 @@ registerTool('create_status_watch', 'Register a webhook watch on a service statu
751
785
  },
752
786
  ],
753
787
  };
754
- });
788
+ }, CREATE_TOOL);
755
789
  // ── Tool: create_digest_watch (1 credit) ────────────────────────────
756
790
  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.', {
757
791
  cadence: z.enum(['daily', 'weekly']).describe('How often the digest fires'),
@@ -773,7 +807,7 @@ registerTool('create_digest_watch', 'Register a scheduled digest webhook that fi
773
807
  },
774
808
  ],
775
809
  };
776
- });
810
+ }, CREATE_TOOL);
777
811
  // ── Tool: create_leaderboard_rank_watch (1 credit) ──────────────────
778
812
  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.', {
779
813
  provider: z.string().describe('Provider name or slug (case-insensitive). e.g. claude, openai, gemini, bedrock, azure'),
@@ -800,14 +834,14 @@ registerTool('create_leaderboard_rank_watch', 'Register a webhook that fires whe
800
834
  },
801
835
  ],
802
836
  };
803
- });
837
+ }, CREATE_TOOL);
804
838
  // ── Tool: delete_watch ──────────────────────────────────────────────
805
839
  registerTool('delete_watch', 'Delete one of your active webhook watches by id. Free, requires TENSORFEED_TOKEN.', {
806
840
  watch_id: z.string().describe('The wat_... id from create_price_watch / create_status_watch / list_watches'),
807
841
  }, async ({ watch_id }) => {
808
842
  await fetchJSON(`/premium/watches/${watch_id}`, { method: 'DELETE', auth: true });
809
843
  return { content: [{ type: 'text', text: `Deleted watch ${watch_id}.` }] };
810
- });
844
+ }, DELETE_TOOL);
811
845
  // ── Tool: probe_latest (free) ───────────────────────────────────────
812
846
  registerTool('probe_latest', 'Last 24 hours of measured LLM endpoint latency and availability per provider (Anthropic, OpenAI, Google, Mistral, Cohere). TensorFeed pings each provider\'s chat completion endpoint every 15 min and records time-to-first-byte, total response time, and HTTP status. Returns per-provider success rate and ttfb/total p50/p95/p99 latency. The data is unique because we measure it ourselves, not self-reported by the providers. Useful when an agent needs to pick a provider whose SLA you can verify, or to detect ongoing incidents before they hit a status page. Free, no auth.', {}, async () => {
813
847
  const data = (await fetchJSON('/probe/latest'));
@@ -886,7 +920,7 @@ registerTool('mcp_registry_snapshot', 'Today\'s summary of the official Model Co
886
920
  content: [
887
921
  {
888
922
  type: 'text',
889
- text: `MCP Server Registry — ${s.date}\n` +
923
+ text: `MCP Server Registry, ${s.date}\n` +
890
924
  `Total servers: ${s.total_servers} (across ${s.total_versions} versioned entries)\n` +
891
925
  `Status: ${statusLine}\n${delta}\n\n` +
892
926
  `Top namespaces:\n${topNs}` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tensorfeed/mcp-server",
3
- "version": "1.24.0",
3
+ "version": "1.25.1",
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",
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "ai.tensorfeed/mcp-server",
4
4
  "title": "TensorFeed",
5
5
  "description": "AI news, model pricing, service status, and machine-payable premium tools. AFTA-certified.",
6
- "version": "1.24.0",
6
+ "version": "1.25.1",
7
7
  "websiteUrl": "https://tensorfeed.ai/developers/agent-payments",
8
8
  "repository": {
9
9
  "url": "https://github.com/RipperMercs/tensorfeed-mcp",
@@ -14,7 +14,7 @@
14
14
  "registryType": "npm",
15
15
  "registryBaseUrl": "https://registry.npmjs.org",
16
16
  "identifier": "@tensorfeed/mcp-server",
17
- "version": "1.24.0",
17
+ "version": "1.25.1",
18
18
  "runtimeHint": "npx",
19
19
  "transport": {
20
20
  "type": "stdio"