@tokenzero/mcp 0.2.10 → 0.2.12
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/bin/tokenzero-mcp.mjs +90 -7
- package/package.json +1 -1
package/bin/tokenzero-mcp.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import readline from "node:readline";
|
|
|
9
9
|
import crypto from "node:crypto";
|
|
10
10
|
|
|
11
11
|
// src/pricing.ts
|
|
12
|
-
var PRICING_VERSION = "2026-
|
|
12
|
+
var PRICING_VERSION = "2026-11-01";
|
|
13
13
|
var P = (provider, tier, input, output, cachedInput, cacheWrite, extra = {}) => ({ provider, tier, input, output, cachedInput, cacheWrite, ...extra });
|
|
14
14
|
var PRICES = {
|
|
15
15
|
// ---- OpenAI (standard tier, ≤272K; cache write = 30m TTL rate) ----
|
|
@@ -23,6 +23,9 @@ var PRICES = {
|
|
|
23
23
|
"gpt-5.6-luna": P("openai", "fast", 0.2, 1.2, 0.02, 0.25, { longCtxAt: 272e3, longCtx: { input: 0.4, output: 1.8, cachedInput: 0.04, cacheWrite: 0.5 }, minCacheTokens: 1024 }),
|
|
24
24
|
// Daybreak cyber model: access needs OpenAI approval; 400K window
|
|
25
25
|
"gpt-5.6-cyber": P("openai", "frontier", 12.5, 75, 1.25, 15.625, { longCtxAt: 272e3, longCtx: { input: 25, output: 112.5, cachedInput: 2.5, cacheWrite: 31.25 }, minCacheTokens: 1024 }),
|
|
26
|
+
// 2026-11-01 (developers.openai.com/api/docs/pricing): Astra for Law at Astra rates (promo), Daybreak gpt-5.5-cyber.
|
|
27
|
+
"gpt-6-astra-law": P("openai", "frontier", 10, 50, 1, void 0, { minCacheTokens: 1024 }),
|
|
28
|
+
"gpt-5.5-cyber": P("openai", "frontier", 12.5, 75),
|
|
26
29
|
"gpt-5.3-codex": P("openai", "mid", 1.75, 14, 0.175, void 0, { minCacheTokens: 1024 }),
|
|
27
30
|
"gpt-5.5": P("openai", "frontier", 5, 30, 0.5, void 0, { fastFactor: 2.5, longCtxAt: 272e3, longCtx: { input: 10, output: 45, cachedInput: 1 }, minCacheTokens: 1024 }),
|
|
28
31
|
"gpt-5.5-pro": P("openai", "frontier", 30, 180),
|
|
@@ -42,6 +45,13 @@ var PRICES = {
|
|
|
42
45
|
"o3-mini": P("openai", "fast", 1.1, 4.4, 0.55, void 0, { minCacheTokens: 1024 }),
|
|
43
46
|
"o4-mini": P("openai", "fast", 1.1, 4.4, 0.275, void 0, { minCacheTokens: 1024 }),
|
|
44
47
|
"o1": P("openai", "frontier", 15, 60, 7.5),
|
|
48
|
+
"o1-pro": P("openai", "frontier", 150, 600),
|
|
49
|
+
// Legacy models still on the pricing page: audited repos use them, so receipts and findings price them correctly
|
|
50
|
+
// instead of falling back to the nearest prefix row (gpt-4o-2024-05-13 is $5/$15, not gpt-4o's $2.50/$10).
|
|
51
|
+
"gpt-4o-2024-05-13": P("openai", "mid", 5, 15),
|
|
52
|
+
"gpt-4-turbo": P("openai", "frontier", 10, 30),
|
|
53
|
+
"gpt-4-0613": P("openai", "frontier", 30, 60),
|
|
54
|
+
"gpt-3.5-turbo": P("openai", "fast", 0.5, 1.5),
|
|
45
55
|
"gpt-4.1": P("openai", "mid", 2, 8, 0.5, void 0, { fastFactor: 1.75, minCacheTokens: 1024 }),
|
|
46
56
|
"gpt-4.1-mini": P("openai", "fast", 0.4, 1.6, 0.1, void 0, { minCacheTokens: 1024 }),
|
|
47
57
|
"gpt-4.1-nano": P("openai", "fast", 0.1, 0.4, 0.025, void 0, { minCacheTokens: 1024 }),
|
|
@@ -85,7 +95,7 @@ var PRICES = {
|
|
|
85
95
|
"deepseek-v4-pro": P("deepseek", "mid", 1.32, 3.96, 0.044),
|
|
86
96
|
"deepseek-flash": P("deepseek", "fast", 0.3, 1.2, 6e-3)
|
|
87
97
|
};
|
|
88
|
-
var MODEL_ALIASES = { "deepseek-v4-flash": "deepseek-flash", "deepseek-v4-flash-vision-exp": "deepseek-flash" };
|
|
98
|
+
var MODEL_ALIASES = { "deepseek-v4-flash": "deepseek-flash", "deepseek-v4-flash-vision-exp": "deepseek-flash", "deepseek-v4.1-flash": "deepseek-flash", "deepseek-v4.1-flash-1017": "deepseek-flash", "deepseek-v4-pro-0813": "deepseek-v4-pro", "gpt-4-turbo-2024-04-09": "gpt-4-turbo", "gpt-4": "gpt-4-0613", "gpt-3.5-turbo-0125": "gpt-3.5-turbo" };
|
|
89
99
|
var CASCADE_TARGET = {
|
|
90
100
|
"gpt-6-astra": "gpt-6.1-sol",
|
|
91
101
|
"gpt-6.1-sol": "gpt-6.1-terra",
|
|
@@ -123,6 +133,7 @@ var CASCADE_TARGET = {
|
|
|
123
133
|
"gemini-2.5-flash": "gemini-2.5-flash-lite"
|
|
124
134
|
};
|
|
125
135
|
var KEYS_BY_LENGTH = Object.keys(PRICES).sort((a, b) => b.length - a.length);
|
|
136
|
+
var DISTINCT_VARIANT = /^(instruct|audio|realtime|search|transcribe|tts|image|embedding|vision|computer-use|deep-research)\b/;
|
|
126
137
|
function normalizeModel(model) {
|
|
127
138
|
if (typeof model !== "string" || !model) return null;
|
|
128
139
|
let m = model.toLowerCase().trim();
|
|
@@ -130,11 +141,12 @@ function normalizeModel(model) {
|
|
|
130
141
|
m = m.replace(/^(us|eu|apac|global)\./, "").replace(/^(anthropic|amazon|meta|mistral|openai)\./, "");
|
|
131
142
|
m = m.replace(/-v\d+(?::\d+)?$/, "");
|
|
132
143
|
if (MODEL_ALIASES[m]) m = MODEL_ALIASES[m];
|
|
144
|
+
if (PRICES[m]) return m;
|
|
133
145
|
m = m.replace(/@\d{8}$/, "");
|
|
134
146
|
m = m.replace(/-(\d{8}|\d{4}-\d{2}-\d{2})$/, "");
|
|
135
147
|
m = m.replace(/-(latest|preview|customtools|thinking)$/, "");
|
|
136
148
|
if (PRICES[m]) return m;
|
|
137
|
-
for (const k of KEYS_BY_LENGTH) if (m.startsWith(k + "-")
|
|
149
|
+
for (const k of KEYS_BY_LENGTH) if (m === k || m.startsWith(k + "-") && !DISTINCT_VARIANT.test(m.slice(k.length + 1))) return k;
|
|
138
150
|
return null;
|
|
139
151
|
}
|
|
140
152
|
var isFreeModel = (model) => typeof model === "string" && (/:free$/i.test(model.trim()) || /^openrouter\/free$/i.test(model.trim()));
|
|
@@ -920,12 +932,58 @@ function manifests(root) {
|
|
|
920
932
|
});
|
|
921
933
|
}
|
|
922
934
|
|
|
935
|
+
// src/events-types.ts
|
|
936
|
+
var EVENTS_EXTENSION = "io.modelcontextprotocol/events";
|
|
937
|
+
var EVENT_ERR = { invalidParams: -32602, notFound: -32023, forbidden: -32024, resourceExhausted: -32025, unsupported: -32026 };
|
|
938
|
+
var keyNote = "Needs a workspace key (x-tokenzero-key or TOKENZERO_KEY).";
|
|
939
|
+
var EVENT_TYPES = [
|
|
940
|
+
{
|
|
941
|
+
name: "spend.threshold_crossed",
|
|
942
|
+
description: `Fires once per period when the workspace's metered LLM spend through TokenZero crosses a dollar threshold: wake an agent on a cost spike. ${keyNote}`,
|
|
943
|
+
delivery: ["poll"],
|
|
944
|
+
inputSchema: { type: "object", properties: { usd: { type: "number", exclusiveMinimum: 0, description: "threshold in USD of actual (post-savings) spend" }, window: { type: "string", enum: ["day", "month"], default: "day", description: "UTC calendar period the spend accumulates over" } }, required: ["usd"] },
|
|
945
|
+
payloadSchema: { type: "object", properties: { workspace: { type: "string" }, window: { type: "string" }, period: { type: "string" }, threshold_usd: { type: "number" }, spend_usd: { type: "number" }, crossed_at: { type: "string", format: "date-time" }, request_id: { type: "string" }, dashboard_url: { type: "string" } } }
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
name: "savings.daily_report",
|
|
949
|
+
description: `One event per completed UTC day with traffic: requests, list-price baseline, actual spend and signed savings. ${keyNote}`,
|
|
950
|
+
delivery: ["poll"],
|
|
951
|
+
inputSchema: { type: "object", properties: {} },
|
|
952
|
+
payloadSchema: { type: "object", properties: { workspace: { type: "string" }, date: { type: "string", format: "date" }, requests: { type: "integer" }, cache_hits: { type: "integer" }, baseline_usd: { type: "number" }, actual_usd: { type: "number" }, savings_usd: { type: "number" }, savings_pct: { type: "number" } } }
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
name: "upstream.auth_failed",
|
|
956
|
+
description: `Fires when a provider rejects the BYOK key a call carried (401/403), with the fix and a keyless sandbox link. ${keyNote}`,
|
|
957
|
+
delivery: ["poll"],
|
|
958
|
+
inputSchema: { type: "object", properties: { provider: { type: "string", description: "only this provider (openai, anthropic, gemini, ...)" } } },
|
|
959
|
+
payloadSchema: { type: "object", properties: { workspace: { type: "string" }, request_id: { type: "string" }, provider: { type: "string" }, status: { type: "integer" }, model: { type: ["string", "null"] }, at: { type: "string", format: "date-time" }, fix: { type: "string" }, keyless_sandbox: { type: "string" } } }
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
name: "pricing.changed",
|
|
963
|
+
description: "Fires when a list price in TokenZero's curated, provider-verified pricing table changes (one event per model and field). No key needed.",
|
|
964
|
+
delivery: ["poll"],
|
|
965
|
+
inputSchema: { type: "object", properties: { models: { type: "array", items: { type: "string" }, description: "only these model ids" } } },
|
|
966
|
+
payloadSchema: { type: "object", properties: { model: { type: "string" }, field: { type: "string", enum: ["input", "output", "cachedInput", "cacheWrite", "added", "removed"] }, old: { type: ["number", "null"] }, new: { type: ["number", "null"] }, unit: { type: "string" }, pricing_version: { type: "string" } } }
|
|
967
|
+
}
|
|
968
|
+
];
|
|
969
|
+
|
|
923
970
|
// mcp/src/main.ts
|
|
924
|
-
var VERSION = "0.2.
|
|
971
|
+
var VERSION = "0.2.12";
|
|
925
972
|
var BAKED_URL = true ? "https://tokenzero.sh" : "__TOKENZERO_DEFAULT_URL__";
|
|
926
973
|
var SUPPORTED = [...MCP_LEGACY_VERSIONS];
|
|
927
974
|
var SERVER = { name: "tokenzero", version: VERSION };
|
|
928
|
-
var CAPABILITIES = { tools: { listChanged: false }, prompts: { listChanged: false } };
|
|
975
|
+
var CAPABILITIES = { tools: { listChanged: false }, prompts: { listChanged: false }, extensions: { [EVENTS_EXTENSION]: {} } };
|
|
976
|
+
var LEGACY_CAPABILITIES = { tools: { listChanged: false }, prompts: { listChanged: false }, experimental: { [EVENTS_EXTENSION]: {} } };
|
|
977
|
+
var listEvents = () => ({ events: EVENT_TYPES.map((e) => ({ ...e, delivery: [...e.delivery] })) });
|
|
978
|
+
async function pollEvents(params) {
|
|
979
|
+
const base = serviceUrl();
|
|
980
|
+
if (!base) return { error: { code: EVENT_ERR.unsupported, message: "Unsupported", data: { reason: "set TOKENZERO_URL to poll events" } } };
|
|
981
|
+
const key = process.env.TOKENZERO_KEY ?? "";
|
|
982
|
+
const r = await fetch(base + "/v1/events/poll", { method: "POST", redirect: "manual", signal: AbortSignal.timeout(2e4), headers: { "content-type": "application/json", "user-agent": `tokenzero-mcp/${VERSION}`, ...key ? { "x-tokenzero-key": key } : {} }, body: JSON.stringify({ name: params?.name, arguments: params?.arguments, cursor: params?.cursor ?? null, maxAgeMs: params?.maxAgeMs, maxEvents: params?.maxEvents }) });
|
|
983
|
+
const j = await r.json().catch(() => null);
|
|
984
|
+
if (r.ok && j && Array.isArray(j.events)) return { result: j };
|
|
985
|
+
return { error: j?.error && typeof j.error.code === "number" ? j.error : { code: RPC.internal, message: `events/poll failed: HTTP ${r.status}` } };
|
|
986
|
+
}
|
|
929
987
|
var PROMPTS = [
|
|
930
988
|
{ name: "cut-my-llm-bill", title: "Cut my LLM bill", description: "Find token waste in this project (scanned locally, nothing uploaded) and show the top fixes with estimated savings. No API key needed.", arguments: [{ name: "repo", description: "owner/name of a public GitHub repo instead of the local project", required: false }] },
|
|
931
989
|
{ name: "price-my-workload", title: "Price my LLM workload", description: "Price a workload across models at list prices, with the cheaper sibling and the effect of prompt caching. No API key needed.", arguments: [{ name: "model", description: "model id, e.g. gpt-6.1-sol or claude-sonnet-5-5", required: true }, { name: "requests_per_month", description: "monthly request volume (default 10000)", required: false }] },
|
|
@@ -965,7 +1023,7 @@ var TOOLS = [
|
|
|
965
1023
|
name: "calculate_model_arbitrage",
|
|
966
1024
|
title: "Price a workload across models",
|
|
967
1025
|
annotations: { title: "Price a workload across models", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
968
|
-
description: `No API key needed; only \`model\` is required (defaults 2,000 tokens in and 400 out). Price a workload at list prices
|
|
1026
|
+
description: `No API key needed; only \`model\` is required (defaults 2,000 tokens in and 400 out). Price a workload at list prices for the requested model, its cheaper sibling and any extra models, with and without prompt caching. Runs locally.`,
|
|
969
1027
|
inputSchema: { type: "object", properties: { model: { type: "string", description: "model id, e.g. gpt-6.1-sol or claude-sonnet-5-5" }, input_tokens: { type: "number", description: "per request; default 2000" }, output_tokens: { type: "number", description: "per request; default 400" }, requests_per_month: { type: "number" }, static_tokens: { type: "number", description: "portion of input_tokens that is a fixed prefix (system + tools)" }, compare_to: { type: "array", items: { type: "string" } } }, required: ["model"], additionalProperties: false }
|
|
970
1028
|
},
|
|
971
1029
|
{
|
|
@@ -1092,6 +1150,16 @@ async function handle(m) {
|
|
|
1092
1150
|
return reply(modernResult(promptMessages(String(m.params?.name ?? ""), m.params?.arguments), SERVER));
|
|
1093
1151
|
case "tools/call":
|
|
1094
1152
|
return reply(modernResult(await call(), SERVER));
|
|
1153
|
+
case "events/list":
|
|
1154
|
+
return reply(modernResult(listEvents(), SERVER, true));
|
|
1155
|
+
case "events/poll": {
|
|
1156
|
+
const r = await pollEvents(m.params);
|
|
1157
|
+
return r.result ? reply({ ...r.result, resultType: "complete", _meta: modernResult({}, SERVER)._meta }) : failData(r.error.code, r.error.message, r.error.data);
|
|
1158
|
+
}
|
|
1159
|
+
case "events/stream":
|
|
1160
|
+
case "events/subscribe":
|
|
1161
|
+
case "events/unsubscribe":
|
|
1162
|
+
return failData(EVENT_ERR.unsupported, "Unsupported", { feature: "deliveryMode", value: m.method === "events/stream" ? "push" : "webhook", supported: ["poll"] });
|
|
1095
1163
|
default:
|
|
1096
1164
|
if (typeof m.method === "string" && m.method.startsWith("notifications/")) return;
|
|
1097
1165
|
return fail(RPC.methodNotFound, `method not found: ${m.method}`);
|
|
@@ -1100,7 +1168,7 @@ async function handle(m) {
|
|
|
1100
1168
|
switch (m?.method) {
|
|
1101
1169
|
case "initialize": {
|
|
1102
1170
|
const want = m.params?.protocolVersion;
|
|
1103
|
-
return reply({ protocolVersion: SUPPORTED.includes(want) ? want : SUPPORTED[0], capabilities:
|
|
1171
|
+
return reply({ protocolVersion: SUPPORTED.includes(want) ? want : SUPPORTED[0], capabilities: LEGACY_CAPABILITIES, serverInfo: SERVER, instructions: INSTRUCTIONS });
|
|
1104
1172
|
}
|
|
1105
1173
|
case "ping":
|
|
1106
1174
|
return reply({});
|
|
@@ -1115,6 +1183,21 @@ async function handle(m) {
|
|
|
1115
1183
|
return reply(promptMessages(String(m.params?.name ?? ""), m.params?.arguments));
|
|
1116
1184
|
case "tools/call":
|
|
1117
1185
|
return reply(await call());
|
|
1186
|
+
case "events/list":
|
|
1187
|
+
return reply(listEvents());
|
|
1188
|
+
case "events/poll": {
|
|
1189
|
+
const r = await pollEvents(m.params);
|
|
1190
|
+
if (r.result) {
|
|
1191
|
+
const { resultType, ...rest } = r.result;
|
|
1192
|
+
void resultType;
|
|
1193
|
+
return reply(rest);
|
|
1194
|
+
}
|
|
1195
|
+
return failData(r.error.code, r.error.message, r.error.data);
|
|
1196
|
+
}
|
|
1197
|
+
case "events/stream":
|
|
1198
|
+
case "events/subscribe":
|
|
1199
|
+
case "events/unsubscribe":
|
|
1200
|
+
return failData(EVENT_ERR.unsupported, "Unsupported", { feature: "deliveryMode", value: m.method === "events/stream" ? "push" : "webhook", supported: ["poll"] });
|
|
1118
1201
|
default:
|
|
1119
1202
|
if (typeof m?.method === "string" && m.method.startsWith("notifications/")) return;
|
|
1120
1203
|
return fail(RPC.methodNotFound, `method not found: ${m?.method}`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenzero/mcp",
|
|
3
3
|
"mcpName": "sh.tokenzero/mcp",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.12",
|
|
5
5
|
"description": "TokenZero MCP server (stdio): audit LLM token waste in your project locally, price model alternatives, and make prompts cache-friendly from Claude Code, Cursor, Windsurf or Cline.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "Apache-2.0",
|