@tensorfeed/mcp-server 1.11.0 → 1.12.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 +11 -1
- package/README.md +23 -107
- package/dist/index.js +46 -27
- package/dist/sanitize.d.ts +40 -0
- package/dist/sanitize.js +0 -0
- package/package.json +19 -3
- package/server.json +4 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
All notable changes to the [TensorFeed.ai MCP server](https://github.com/RipperMercs/tensorfeed
|
|
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
|
+
|
|
5
|
+
## 1.12.0 - 2026-05-04
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Defense-in-depth output sanitizer (`src/sanitize.ts`). Every tool's text output passes through a scrub pass before reaching the host LLM (Claude Desktop, Cursor, Cline, Roo, etc): strips ASCII control chars (preserves newline, carriage return, tab), strips bidi control + zero-width spoofing chars, and neutralizes role-confusion markers (ChatML / Llama / Mistral chat tokens, "ignore previous instructions", line-leading "system:" / "developer:" / "assistant:" preambles). Wired via a new `registerTool()` helper that wraps `server.tool()`. The TensorFeed worker already runs the same scrub on its agent-facing endpoints; this layer protects against any new endpoint that might forget it, plus drift between worker rules and what the MCP server should consider safe. No behavior change for legitimate text. No new dependencies.
|
|
9
|
+
|
|
10
|
+
## 1.11.1 - 2026-05-04
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Repository URL updated to point at the dedicated [`RipperMercs/tensorfeed-mcp`](https://github.com/RipperMercs/tensorfeed-mcp) repo. The MCP server now has its own GitHub home for issues, stars, and discoverability. Source-of-truth for builds remains in the main `tensorfeed` monorepo's `mcp-server/` folder; the standalone repo is mirrored on release. No code changes; no behavior changes; npm install / npx unaffected.
|
|
4
14
|
|
|
5
15
|
## 1.11.0 - 2026-04-29
|
|
6
16
|
|
package/README.md
CHANGED
|
@@ -1,118 +1,34 @@
|
|
|
1
1
|
# TensorFeed MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **The MCP server has its own repo: https://github.com/RipperMercs/tensorfeed-mcp**
|
|
4
|
+
>
|
|
5
|
+
> User-facing docs, install instructions, and the full tool reference live there. Star and watch that repo to follow MCP server updates.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
This subfolder remains in the main `tensorfeed` repo as the **publishing source** for the npm package (`@tensorfeed/mcp-server`) and the [official MCP registry](https://registry.modelcontextprotocol.io/v0/servers/ai.tensorfeed/mcp-server) entry. Edits to `src/`, `server.json`, `package.json`, etc. happen here and get pushed to the standalone repo on release.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|------|-------------|
|
|
9
|
-
| `get_ai_news` | Latest AI news from 15+ sources (filterable by category) |
|
|
10
|
-
| `get_ai_status` | Real-time status of Claude, OpenAI, Gemini, Mistral, and more |
|
|
11
|
-
| `is_service_down` | Check if a specific AI service is down |
|
|
12
|
-
| `get_model_pricing` | Compare pricing across all major AI model providers |
|
|
13
|
-
| `get_ai_today` | Summary of top AI stories from the last 24 hours |
|
|
14
|
-
| `get_agent_activity` | Live AI bot traffic on TensorFeed.ai with top-bot breakdown |
|
|
15
|
-
| `mcp_registry_snapshot` | Today's summary of the official MCP server registry: total servers, top namespaces, newly added/reactivated/deprecated names |
|
|
16
|
-
| `probe_latest` | Last 24h of measured LLM endpoint latency and availability per provider (ttfb/total p50/p95/p99). Unique because measured, not self-reported |
|
|
9
|
+
## Publish a new version
|
|
17
10
|
|
|
18
|
-
|
|
11
|
+
From the main tensorfeed repo:
|
|
19
12
|
|
|
20
|
-
|
|
13
|
+
```powershell
|
|
14
|
+
# 1. Bump the version in mcp-server/package.json + mcp-server/server.json
|
|
15
|
+
# 2. Build + npm publish from the mcp-server/ folder
|
|
16
|
+
cd mcp-server
|
|
17
|
+
npm run build
|
|
18
|
+
npm publish --access public
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
| `get_account_balance` | Free | Show credits remaining on the configured token |
|
|
25
|
-
| `get_account_usage` | Free | Per-endpoint usage history for the configured token |
|
|
26
|
-
| `premium_routing` | 1 credit | Top-N ranked AI model recommendations with full score breakdown |
|
|
27
|
-
| `pricing_series` | 1 credit | Daily price points for one model with min/max/delta summary |
|
|
28
|
-
| `benchmark_series` | 1 credit | Score evolution for a benchmark on one model |
|
|
29
|
-
| `status_uptime` | 1 credit | Daily uptime % for one provider with incident-day list |
|
|
30
|
-
| `premium_agents_directory` | 1 credit | Enriched agents catalog with live status, news, traffic, trending_score |
|
|
31
|
-
| `news_search` | 1 credit | Full-text news search with date/provider/category filters and relevance scoring |
|
|
32
|
-
| `cost_projection` | 1 credit | Project workload cost across 1-10 AI models with 4 time horizons and cheapest-monthly ranking |
|
|
33
|
-
| `provider_deepdive` | 1 credit | One provider's full profile: status, all models with benchmarks joined, recent news, traffic |
|
|
34
|
-
| `compare_models` | 1 credit | Side-by-side comparison of 2-5 models with normalized benchmarks and rankings |
|
|
35
|
-
| `whats_new` | 1 credit | Agent morning brief: pricing changes, incidents, top news from last 1-7 days |
|
|
36
|
-
| `mcp_registry_series` | 1 credit | Multi-day MCP registry growth and churn series, 90-day max range |
|
|
37
|
-
| `probe_series` | 1 credit | Daily SLA series for one LLM provider with ttfb/total p50/p95/p99 and incident-hour count, 90-day max range |
|
|
38
|
-
| `list_watches` | Free | List active webhook watches owned by the token |
|
|
39
|
-
| `create_price_watch` | 1 credit | Register a webhook watch on a model price change |
|
|
40
|
-
| `create_status_watch` | 1 credit | Register a webhook watch on a service status transition |
|
|
41
|
-
| `create_digest_watch` | 1 credit | Register a daily/weekly digest webhook (pricing-changes summary, set-and-forget) |
|
|
42
|
-
| `delete_watch` | Free | Remove an active watch |
|
|
20
|
+
# 3. Republish to the official MCP registry
|
|
21
|
+
.\scripts\mcp-publish.ps1
|
|
43
22
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Add to your Claude Code MCP config (`~/.claude/claude_desktop_config.json`):
|
|
47
|
-
|
|
48
|
-
```json
|
|
49
|
-
{
|
|
50
|
-
"mcpServers": {
|
|
51
|
-
"tensorfeed": {
|
|
52
|
-
"command": "npx",
|
|
53
|
-
"args": ["-y", "@tensorfeed/mcp-server"]
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
To enable premium tools, add the `env` block:
|
|
60
|
-
|
|
61
|
-
```json
|
|
62
|
-
{
|
|
63
|
-
"mcpServers": {
|
|
64
|
-
"tensorfeed": {
|
|
65
|
-
"command": "npx",
|
|
66
|
-
"args": ["-y", "@tensorfeed/mcp-server"],
|
|
67
|
-
"env": {
|
|
68
|
-
"TENSORFEED_TOKEN": "tf_live_..."
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Setup with Claude Desktop
|
|
76
|
-
|
|
77
|
-
Add to Claude Desktop settings (same shape as above).
|
|
78
|
-
|
|
79
|
-
## Run Locally
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
npx @tensorfeed/mcp-server
|
|
83
|
-
# or with a token for premium endpoints
|
|
84
|
-
TENSORFEED_TOKEN=tf_live_... npx @tensorfeed/mcp-server
|
|
23
|
+
# 4. Mirror the new version to the standalone repo
|
|
24
|
+
# (manual until we wire it as a release-time GitHub Action)
|
|
85
25
|
```
|
|
86
26
|
|
|
87
|
-
##
|
|
88
|
-
|
|
89
|
-
Free tier:
|
|
90
|
-
|
|
91
|
-
- "What's happening in AI today?"
|
|
92
|
-
- "Is Claude down right now?"
|
|
93
|
-
- "Compare pricing between Claude Opus and GPT-4o"
|
|
94
|
-
- "Show me the latest AI research papers"
|
|
95
|
-
- "What's the status of all AI services?"
|
|
96
|
-
|
|
97
|
-
Premium tier (with `TENSORFEED_TOKEN` set):
|
|
98
|
-
|
|
99
|
-
- "Recommend the best AI model for code under $5 per million tokens"
|
|
100
|
-
- "Show the price history of Claude Opus 4.7 over the last 30 days"
|
|
101
|
-
- "What's Anthropic's uptime this month?"
|
|
102
|
-
- "Compare model pricing between April 1 and today"
|
|
103
|
-
- "Set up a webhook to my server when Claude goes down"
|
|
104
|
-
- "What's my credit balance?"
|
|
105
|
-
|
|
106
|
-
## Pricing
|
|
107
|
-
|
|
108
|
-
Premium tools cost 1 credit per call. Credits are 50 per $1 USDC at base rate, with volume discounts at $5 (10%), $30 (25%), and $200 (40%). See [tensorfeed.ai/developers/agent-payments](https://tensorfeed.ai/developers/agent-payments) for the full payment flow and trust attestation.
|
|
109
|
-
|
|
110
|
-
No refunds: all credit purchases are final per Section 17.5 of the [Terms](https://tensorfeed.ai/terms#premium). Credits do not expire and are jointly redeemable on tensorfeed.ai and terminalfeed.io, so the safe pattern is to buy small ($1 USDC for 50 credits) and top up as call volume is calibrated. Premium access is unavailable in OFAC-sanctioned jurisdictions; see Section 17.9.
|
|
111
|
-
|
|
112
|
-
## Data Source
|
|
113
|
-
|
|
114
|
-
All data comes from the [TensorFeed.ai API](https://tensorfeed.ai/developers), which aggregates from 15+ sources including Anthropic, OpenAI, Google AI, TechCrunch, The Verge, arXiv, Hacker News, and more. Updated every 10 minutes.
|
|
115
|
-
|
|
116
|
-
## License
|
|
27
|
+
## Quick links
|
|
117
28
|
|
|
118
|
-
|
|
29
|
+
- **User-facing repo (please star):** https://github.com/RipperMercs/tensorfeed-mcp
|
|
30
|
+
- **npm package:** https://www.npmjs.com/package/@tensorfeed/mcp-server
|
|
31
|
+
- **Official MCP registry:** https://registry.modelcontextprotocol.io/v0/servers/ai.tensorfeed/mcp-server
|
|
32
|
+
- **TensorFeed.ai:** https://tensorfeed.ai
|
|
33
|
+
- **Premium / payments:** https://tensorfeed.ai/developers/agent-payments
|
|
34
|
+
- **AFTA standard:** https://tensorfeed.ai/agent-fair-trade
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
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 { sanitizeToolResponse } from './sanitize.js';
|
|
5
6
|
const API_BASE = 'https://tensorfeed.ai/api';
|
|
6
7
|
const SDK_VERSION = '1.9.0';
|
|
7
8
|
async function fetchJSON(path, opts = {}) {
|
|
@@ -46,8 +47,26 @@ const server = new McpServer({
|
|
|
46
47
|
name: 'tensorfeed',
|
|
47
48
|
version: SDK_VERSION,
|
|
48
49
|
});
|
|
50
|
+
// ── Defense-in-depth tool wrapper ───────────────────────────────────
|
|
51
|
+
// Every tool's text output passes through sanitizeToolResponse before
|
|
52
|
+
// reaching the host LLM. Strips control chars, bidi/zero-width spoofing,
|
|
53
|
+
// and neutralizes role-confusion tokens (ChatML / Llama / Mistral chat
|
|
54
|
+
// markers, "ignore previous instructions", etc). The TensorFeed worker
|
|
55
|
+
// already runs the same scrub on its agent-facing endpoints; this layer
|
|
56
|
+
// covers any new endpoint that might forget it, plus protects against
|
|
57
|
+
// drift between worker rules and what the MCP server should consider
|
|
58
|
+
// safe. See ./sanitize.ts.
|
|
59
|
+
function registerTool(name, description, paramsSchema, cb) {
|
|
60
|
+
const wrapped = (async (args, extra) => {
|
|
61
|
+
const result = await cb(args, extra);
|
|
62
|
+
return sanitizeToolResponse(result);
|
|
63
|
+
});
|
|
64
|
+
// Direct dispatch to the SDK's underlying registration. Do not call
|
|
65
|
+
// registerTool() here or it will recurse forever.
|
|
66
|
+
return server.tool(name, description, paramsSchema, wrapped);
|
|
67
|
+
}
|
|
49
68
|
// ── Tool: get_ai_news ───────────────────────────────────────────────
|
|
50
|
-
|
|
69
|
+
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.', {
|
|
51
70
|
category: z.string().optional().describe('Filter by category (e.g. "anthropic", "openai", "research", "tools")'),
|
|
52
71
|
limit: z.number().min(1).max(50).optional().describe('Number of articles to return (default 10, max 50)'),
|
|
53
72
|
}, async ({ category, limit }) => {
|
|
@@ -62,7 +81,7 @@ server.tool('get_ai_news', 'Get the latest AI news articles from TensorFeed.ai.
|
|
|
62
81
|
return { content: [{ type: 'text', text: text || 'No articles found.' }] };
|
|
63
82
|
});
|
|
64
83
|
// ── Tool: get_ai_status ─────────────────────────────────────────────
|
|
65
|
-
|
|
84
|
+
registerTool('get_ai_status', 'Get real-time operational status of major AI services (Claude, OpenAI, Gemini, Mistral, Cohere, Replicate, Hugging Face).', {}, async () => {
|
|
66
85
|
const data = await fetchJSON('/status');
|
|
67
86
|
const text = data.services
|
|
68
87
|
.map(s => {
|
|
@@ -75,7 +94,7 @@ server.tool('get_ai_status', 'Get real-time operational status of major AI servi
|
|
|
75
94
|
return { content: [{ type: 'text', text: `AI Service Status:\n${text}` }] };
|
|
76
95
|
});
|
|
77
96
|
// ── Tool: is_service_down ───────────────────────────────────────────
|
|
78
|
-
|
|
97
|
+
registerTool('is_service_down', 'Check if a specific AI service is currently down or experiencing issues.', {
|
|
79
98
|
service: z.string().describe('Service name to check (e.g. "claude", "openai", "gemini", "mistral", "cohere", "hugging face", "replicate")'),
|
|
80
99
|
}, async ({ service }) => {
|
|
81
100
|
const data = await fetchJSON('/status');
|
|
@@ -96,7 +115,7 @@ server.tool('is_service_down', 'Check if a specific AI service is currently down
|
|
|
96
115
|
};
|
|
97
116
|
});
|
|
98
117
|
// ── Tool: get_model_pricing ─────────────────────────────────────────
|
|
99
|
-
|
|
118
|
+
registerTool('get_model_pricing', 'Get AI model pricing comparison across all major providers (Anthropic, OpenAI, Google, Meta, Mistral, Cohere). Prices per 1M tokens.', {}, async () => {
|
|
100
119
|
const data = await fetchJSON('/models');
|
|
101
120
|
const text = data.providers
|
|
102
121
|
.map(p => {
|
|
@@ -116,7 +135,7 @@ server.tool('get_model_pricing', 'Get AI model pricing comparison across all maj
|
|
|
116
135
|
return { content: [{ type: 'text', text: `AI Model Pricing (per 1M tokens):\n\n${text}` }] };
|
|
117
136
|
});
|
|
118
137
|
// ── Tool: get_ai_today ──────────────────────────────────────────────
|
|
119
|
-
|
|
138
|
+
registerTool('get_ai_today', 'Get a summary of what happened in AI today. Returns the top stories from the last 24 hours.', {
|
|
120
139
|
limit: z.number().min(1).max(20).optional().describe('Number of stories (default 5)'),
|
|
121
140
|
}, async ({ limit }) => {
|
|
122
141
|
const data = await fetchJSON(`/news?limit=${limit || 5}`);
|
|
@@ -126,7 +145,7 @@ server.tool('get_ai_today', 'Get a summary of what happened in AI today. Returns
|
|
|
126
145
|
return { content: [{ type: 'text', text: `Today in AI:\n\n${text}` }] };
|
|
127
146
|
});
|
|
128
147
|
// ── Tool: get_agent_activity ────────────────────────────────────────
|
|
129
|
-
|
|
148
|
+
registerTool('get_agent_activity', 'Get live AI bot traffic on TensorFeed.ai. Returns today\'s bot hit count, the most recent 50 hits with bot name + endpoint + timestamp, and a derived top-bots breakdown. Useful when an agent wants to see which crawlers are pulling AI ecosystem data and how that distribution shifts. Free, no auth.', {}, async () => {
|
|
130
149
|
const data = (await fetchJSON('/agents/activity'));
|
|
131
150
|
const tally = new Map();
|
|
132
151
|
for (const hit of data.recent) {
|
|
@@ -158,7 +177,7 @@ server.tool('get_agent_activity', 'Get live AI bot traffic on TensorFeed.ai. Ret
|
|
|
158
177
|
// PREMIUM TOOLS (require TENSORFEED_TOKEN env var, paid in USDC on Base)
|
|
159
178
|
// ════════════════════════════════════════════════════════════════════
|
|
160
179
|
// ── Tool: get_account_balance ───────────────────────────────────────
|
|
161
|
-
|
|
180
|
+
registerTool('get_account_balance', 'Check the credit balance for the configured TensorFeed bearer token. Free, but requires TENSORFEED_TOKEN to be set.', {}, async () => {
|
|
162
181
|
const data = (await fetchJSON('/payment/balance', { auth: true }));
|
|
163
182
|
return {
|
|
164
183
|
content: [
|
|
@@ -170,7 +189,7 @@ server.tool('get_account_balance', 'Check the credit balance for the configured
|
|
|
170
189
|
};
|
|
171
190
|
});
|
|
172
191
|
// ── Tool: get_account_usage ─────────────────────────────────────────
|
|
173
|
-
|
|
192
|
+
registerTool('get_account_usage', 'Show per-endpoint usage for the configured TensorFeed token (last 100 calls aggregated). Free, but requires TENSORFEED_TOKEN.', {}, async () => {
|
|
174
193
|
const data = (await fetchJSON('/payment/usage', { auth: true }));
|
|
175
194
|
if (data.total_calls === 0) {
|
|
176
195
|
return { content: [{ type: 'text', text: 'No premium API calls on this token yet.' }] };
|
|
@@ -189,7 +208,7 @@ server.tool('get_account_usage', 'Show per-endpoint usage for the configured Ten
|
|
|
189
208
|
};
|
|
190
209
|
});
|
|
191
210
|
// ── Tool: premium_routing (1 credit) ────────────────────────────────
|
|
192
|
-
|
|
211
|
+
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.', {
|
|
193
212
|
task: z.enum(['code', 'reasoning', 'creative', 'general']).optional().describe('Task type the model needs to be good at (default: general)'),
|
|
194
213
|
budget: z.number().optional().describe('Max blended USD per 1M tokens'),
|
|
195
214
|
min_quality: z.number().min(0).max(1).optional().describe('Minimum quality score in [0, 1]'),
|
|
@@ -217,7 +236,7 @@ server.tool('premium_routing', 'Get a ranked list of recommended AI models for a
|
|
|
217
236
|
return { content: [{ type: 'text', text: `Routing for "${data.task}":\n\n${list}${billing}` }] };
|
|
218
237
|
});
|
|
219
238
|
// ── Tool: pricing_series (1 credit) ─────────────────────────────────
|
|
220
|
-
|
|
239
|
+
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.', {
|
|
221
240
|
model: z.string().describe('Model id or display name (e.g. "Claude Opus 4.7" or "claude-opus-4-7")'),
|
|
222
241
|
from: z.string().optional().describe('Start date YYYY-MM-DD UTC (default: 30 days ago)'),
|
|
223
242
|
to: z.string().optional().describe('End date YYYY-MM-DD UTC (default: today)'),
|
|
@@ -242,7 +261,7 @@ server.tool('pricing_series', 'Daily price points for one AI model with min/max/
|
|
|
242
261
|
};
|
|
243
262
|
});
|
|
244
263
|
// ── Tool: benchmark_series (1 credit) ───────────────────────────────
|
|
245
|
-
|
|
264
|
+
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.', {
|
|
246
265
|
model: z.string().describe('Model id or display name'),
|
|
247
266
|
benchmark: z.string().describe('Benchmark key (e.g. swe_bench, mmlu_pro, gpqa_diamond, math, human_eval)'),
|
|
248
267
|
from: z.string().optional().describe('Start date YYYY-MM-DD UTC (default: 30 days ago)'),
|
|
@@ -268,7 +287,7 @@ server.tool('benchmark_series', 'Score evolution for a single benchmark on one A
|
|
|
268
287
|
};
|
|
269
288
|
});
|
|
270
289
|
// ── Tool: status_uptime (1 credit) ──────────────────────────────────
|
|
271
|
-
|
|
290
|
+
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.', {
|
|
272
291
|
provider: z.string().describe('Provider name (e.g. anthropic, openai, google)'),
|
|
273
292
|
from: z.string().optional().describe('Start date YYYY-MM-DD UTC (default: 30 days ago)'),
|
|
274
293
|
to: z.string().optional().describe('End date YYYY-MM-DD UTC (default: today)'),
|
|
@@ -295,7 +314,7 @@ server.tool('status_uptime', 'Daily uptime rollup for one provider with operatio
|
|
|
295
314
|
};
|
|
296
315
|
});
|
|
297
316
|
// ── Tool: premium_agents_directory (1 credit) ───────────────────────
|
|
298
|
-
|
|
317
|
+
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.', {
|
|
299
318
|
category: z.string().optional().describe('coding, research, general, creative, frameworks'),
|
|
300
319
|
status: z.enum(['operational', 'degraded', 'down', 'unknown']).optional(),
|
|
301
320
|
open_source: z.boolean().optional(),
|
|
@@ -328,7 +347,7 @@ server.tool('premium_agents_directory', 'Enriched AI agents catalog joined with
|
|
|
328
347
|
};
|
|
329
348
|
});
|
|
330
349
|
// ── Tool: whats_new (1 credit) ──────────────────────────────────────
|
|
331
|
-
|
|
350
|
+
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.', {
|
|
332
351
|
days: z.number().min(1).max(7).optional().describe('Window length in days (default 1)'),
|
|
333
352
|
news_limit: z.number().min(1).max(25).optional().describe('Max news headlines (default 10)'),
|
|
334
353
|
}, async ({ days, news_limit }) => {
|
|
@@ -364,7 +383,7 @@ server.tool('whats_new', 'Agent morning brief: pricing changes, new/removed mode
|
|
|
364
383
|
};
|
|
365
384
|
});
|
|
366
385
|
// ── Tool: compare_models (1 credit) ─────────────────────────────────
|
|
367
|
-
|
|
386
|
+
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.', {
|
|
368
387
|
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"'),
|
|
369
388
|
}, async ({ ids }) => {
|
|
370
389
|
const data = (await fetchJSON(`/premium/compare/models?ids=${encodeURIComponent(ids)}`, { auth: true }));
|
|
@@ -391,7 +410,7 @@ server.tool('compare_models', 'Side-by-side comparison of 2-5 AI models. Returns
|
|
|
391
410
|
};
|
|
392
411
|
});
|
|
393
412
|
// ── Tool: provider_deepdive (1 credit) ──────────────────────────────
|
|
394
|
-
|
|
413
|
+
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.', {
|
|
395
414
|
provider: z.string().describe('Provider id or display name (case-insensitive). Examples: anthropic, openai, google, mistral, cohere'),
|
|
396
415
|
}, async ({ provider }) => {
|
|
397
416
|
const data = (await fetchJSON(`/premium/providers/${encodeURIComponent(provider)}`, { auth: true }));
|
|
@@ -421,7 +440,7 @@ server.tool('provider_deepdive', 'Everything about an AI provider in one call: l
|
|
|
421
440
|
};
|
|
422
441
|
});
|
|
423
442
|
// ── Tool: cost_projection (1 credit) ────────────────────────────────
|
|
424
|
-
|
|
443
|
+
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.', {
|
|
425
444
|
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.'),
|
|
426
445
|
input_tokens_per_day: z.number().min(0).describe('Expected daily input token volume'),
|
|
427
446
|
output_tokens_per_day: z.number().min(0).describe('Expected daily output token volume'),
|
|
@@ -456,7 +475,7 @@ server.tool('cost_projection', 'Project the cost of a token-usage workload acros
|
|
|
456
475
|
};
|
|
457
476
|
});
|
|
458
477
|
// ── Tool: news_search (1 credit) ────────────────────────────────────
|
|
459
|
-
|
|
478
|
+
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.', {
|
|
460
479
|
q: z.string().optional().describe('Free-text query, e.g. "claude opus pricing". Omit to browse latest filtered articles.'),
|
|
461
480
|
from: z.string().optional().describe('Start date YYYY-MM-DD UTC (inclusive)'),
|
|
462
481
|
to: z.string().optional().describe('End date YYYY-MM-DD UTC (inclusive end-of-day)'),
|
|
@@ -494,7 +513,7 @@ server.tool('news_search', 'Full-text search over the TensorFeed news article co
|
|
|
494
513
|
};
|
|
495
514
|
});
|
|
496
515
|
// ── Tool: list_watches ──────────────────────────────────────────────
|
|
497
|
-
|
|
516
|
+
registerTool('list_watches', 'List the active webhook watches owned by the configured TensorFeed token. Free, requires TENSORFEED_TOKEN.', {}, async () => {
|
|
498
517
|
const data = (await fetchJSON('/premium/watches', { auth: true }));
|
|
499
518
|
if (data.count === 0) {
|
|
500
519
|
return { content: [{ type: 'text', text: 'No active watches.' }] };
|
|
@@ -510,7 +529,7 @@ server.tool('list_watches', 'List the active webhook watches owned by the config
|
|
|
510
529
|
return { content: [{ type: 'text', text: `${data.count} active watches:\n\n${rows}` }] };
|
|
511
530
|
});
|
|
512
531
|
// ── Tool: create_price_watch (1 credit) ─────────────────────────────
|
|
513
|
-
|
|
532
|
+
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.', {
|
|
514
533
|
model: z.string().describe('Model name (e.g. "Claude Opus 4.7")'),
|
|
515
534
|
field: z.enum(['inputPrice', 'outputPrice', 'blended']).describe('Which price field to watch'),
|
|
516
535
|
op: z.enum(['lt', 'gt', 'changes']).describe('Trigger: lt = below threshold, gt = above, changes = any change'),
|
|
@@ -535,7 +554,7 @@ server.tool('create_price_watch', 'Register a webhook watch on a model price cha
|
|
|
535
554
|
};
|
|
536
555
|
});
|
|
537
556
|
// ── Tool: create_status_watch (1 credit) ────────────────────────────
|
|
538
|
-
|
|
557
|
+
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.', {
|
|
539
558
|
provider: z.string().describe('Provider name (e.g. anthropic, openai)'),
|
|
540
559
|
op: z.enum(['becomes', 'changes']).describe('becomes = transitions to a specific value; changes = any transition'),
|
|
541
560
|
value: z.enum(['operational', 'degraded', 'down']).optional().describe('Required when op is becomes'),
|
|
@@ -559,7 +578,7 @@ server.tool('create_status_watch', 'Register a webhook watch on a service status
|
|
|
559
578
|
};
|
|
560
579
|
});
|
|
561
580
|
// ── Tool: create_digest_watch (1 credit) ────────────────────────────
|
|
562
|
-
|
|
581
|
+
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.', {
|
|
563
582
|
cadence: z.enum(['daily', 'weekly']).describe('How often the digest fires'),
|
|
564
583
|
callback_url: z.string().describe('HTTPS URL to POST to when the digest fires'),
|
|
565
584
|
secret: z.string().optional().describe('Optional HMAC shared secret'),
|
|
@@ -581,14 +600,14 @@ server.tool('create_digest_watch', 'Register a scheduled digest webhook that fir
|
|
|
581
600
|
};
|
|
582
601
|
});
|
|
583
602
|
// ── Tool: delete_watch ──────────────────────────────────────────────
|
|
584
|
-
|
|
603
|
+
registerTool('delete_watch', 'Delete one of your active webhook watches by id. Free, requires TENSORFEED_TOKEN.', {
|
|
585
604
|
watch_id: z.string().describe('The wat_... id from create_price_watch / create_status_watch / list_watches'),
|
|
586
605
|
}, async ({ watch_id }) => {
|
|
587
606
|
await fetchJSON(`/premium/watches/${watch_id}`, { method: 'DELETE', auth: true });
|
|
588
607
|
return { content: [{ type: 'text', text: `Deleted watch ${watch_id}.` }] };
|
|
589
608
|
});
|
|
590
609
|
// ── Tool: probe_latest (free) ───────────────────────────────────────
|
|
591
|
-
|
|
610
|
+
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 () => {
|
|
592
611
|
const data = (await fetchJSON('/probe/latest'));
|
|
593
612
|
const s = data.summary;
|
|
594
613
|
if (s.providers.length === 0) {
|
|
@@ -614,7 +633,7 @@ server.tool('probe_latest', 'Last 24 hours of measured LLM endpoint latency and
|
|
|
614
633
|
};
|
|
615
634
|
});
|
|
616
635
|
// ── Tool: probe_series (1 credit) ───────────────────────────────────
|
|
617
|
-
|
|
636
|
+
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.', {
|
|
618
637
|
provider: z.enum(['anthropic', 'openai', 'google', 'mistral', 'cohere']).describe('LLM provider key'),
|
|
619
638
|
from: z.string().optional().describe('Inclusive start YYYY-MM-DD (default: 30 days before to)'),
|
|
620
639
|
to: z.string().optional().describe('Inclusive end YYYY-MM-DD (default: today UTC)'),
|
|
@@ -645,7 +664,7 @@ server.tool('probe_series', 'Daily SLA series for one LLM provider, measured by
|
|
|
645
664
|
};
|
|
646
665
|
});
|
|
647
666
|
// ── Tool: mcp_registry_snapshot (free) ──────────────────────────────
|
|
648
|
-
|
|
667
|
+
registerTool('mcp_registry_snapshot', 'Today\'s summary of the official Model Context Protocol server registry. Returns total servers, by-status breakdown, top namespaces, and 1-day deltas (newly added, reactivated, deprecated). Captured daily at 9:30 AM UTC from registry.modelcontextprotocol.io. Useful when an agent wants to see how the MCP ecosystem is growing or detect freshly-deprecated servers it may be using. Free, no auth.', {}, async () => {
|
|
649
668
|
const data = (await fetchJSON('/mcp/registry/snapshot'));
|
|
650
669
|
const s = data.summary;
|
|
651
670
|
const statusLine = Object.entries(s.by_status)
|
|
@@ -676,7 +695,7 @@ server.tool('mcp_registry_snapshot', 'Today\'s summary of the official Model Con
|
|
|
676
695
|
};
|
|
677
696
|
});
|
|
678
697
|
// ── Tool: mcp_registry_series (1 credit) ────────────────────────────
|
|
679
|
-
|
|
698
|
+
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.', {
|
|
680
699
|
from: z.string().optional().describe('Inclusive start date YYYY-MM-DD (default: 30 days before to)'),
|
|
681
700
|
to: z.string().optional().describe('Inclusive end date YYYY-MM-DD (default: today UTC)'),
|
|
682
701
|
}, async ({ from, to }) => {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defense-in-depth output scrub for tool responses.
|
|
3
|
+
*
|
|
4
|
+
* The MCP server returns text from the TensorFeed worker into a host LLM
|
|
5
|
+
* (Claude Desktop, Cursor, Cline, Roo, etc). Any text we forward becomes
|
|
6
|
+
* untrusted input to that downstream model. The worker already runs
|
|
7
|
+
* sanitize.ts on its agent-facing endpoints, but this layer protects
|
|
8
|
+
* against:
|
|
9
|
+
*
|
|
10
|
+
* 1. New worker endpoints that forget the worker-side scrub
|
|
11
|
+
* 2. Future tools that aggregate or reformat upstream text
|
|
12
|
+
* 3. Drift between the worker's sanitization rules and what the MCP
|
|
13
|
+
* server should consider safe
|
|
14
|
+
*
|
|
15
|
+
* The rule set mirrors worker/src/sanitize.ts:
|
|
16
|
+
* - Strip ASCII control chars (preserves \n \r \t)
|
|
17
|
+
* - Strip bidi controls and zero-width chars used in spoofing
|
|
18
|
+
* - Neutralize role-confusion markers (ChatML / Llama / Mistral chat
|
|
19
|
+
* tokens, jailbreak preamble lines, "ignore previous instructions")
|
|
20
|
+
* - Collapse runs of whitespace
|
|
21
|
+
*
|
|
22
|
+
* No length cap is applied here. Tool output is conversational and may
|
|
23
|
+
* legitimately be tens of KB; upstream KV size limits already bound it.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Scrub a single string for return to a host LLM. Idempotent and pure.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sanitizeForLLM(input: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Recursively scrub the `text` field of every text-content item in an
|
|
31
|
+
* MCP tool response. Non-text items (resources, images) pass through
|
|
32
|
+
* untouched. Returns a new content array; the input is not mutated.
|
|
33
|
+
*/
|
|
34
|
+
export declare function sanitizeToolResponse<T extends {
|
|
35
|
+
content: Array<{
|
|
36
|
+
type: string;
|
|
37
|
+
text?: string;
|
|
38
|
+
[k: string]: unknown;
|
|
39
|
+
}>;
|
|
40
|
+
}>(response: T): T;
|
package/dist/sanitize.js
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tensorfeed/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.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",
|
|
@@ -19,13 +19,29 @@
|
|
|
19
19
|
"build": "tsc",
|
|
20
20
|
"start": "node dist/index.js"
|
|
21
21
|
},
|
|
22
|
-
"keywords": [
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"ai",
|
|
25
|
+
"news",
|
|
26
|
+
"tensorfeed",
|
|
27
|
+
"model-context-protocol",
|
|
28
|
+
"claude",
|
|
29
|
+
"agents",
|
|
30
|
+
"x402",
|
|
31
|
+
"usdc",
|
|
32
|
+
"base",
|
|
33
|
+
"model-routing",
|
|
34
|
+
"agent-payments"
|
|
35
|
+
],
|
|
23
36
|
"author": "TensorFeed.ai",
|
|
24
37
|
"license": "MIT",
|
|
25
38
|
"homepage": "https://tensorfeed.ai/developers",
|
|
26
39
|
"repository": {
|
|
27
40
|
"type": "git",
|
|
28
|
-
"url": "https://github.com/RipperMercs/tensorfeed"
|
|
41
|
+
"url": "https://github.com/RipperMercs/tensorfeed-mcp"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/RipperMercs/tensorfeed-mcp/issues"
|
|
29
45
|
},
|
|
30
46
|
"dependencies": {
|
|
31
47
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
package/server.json
CHANGED
|
@@ -3,19 +3,18 @@
|
|
|
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.
|
|
6
|
+
"version": "1.12.0",
|
|
7
7
|
"websiteUrl": "https://tensorfeed.ai/developers/agent-payments",
|
|
8
8
|
"repository": {
|
|
9
|
-
"url": "https://github.com/RipperMercs/tensorfeed",
|
|
10
|
-
"source": "github"
|
|
11
|
-
"subfolder": "mcp-server"
|
|
9
|
+
"url": "https://github.com/RipperMercs/tensorfeed-mcp",
|
|
10
|
+
"source": "github"
|
|
12
11
|
},
|
|
13
12
|
"packages": [
|
|
14
13
|
{
|
|
15
14
|
"registryType": "npm",
|
|
16
15
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
17
16
|
"identifier": "@tensorfeed/mcp-server",
|
|
18
|
-
"version": "1.
|
|
17
|
+
"version": "1.12.0",
|
|
19
18
|
"runtimeHint": "npx",
|
|
20
19
|
"transport": {
|
|
21
20
|
"type": "stdio"
|