@tokenzero/mcp 0.2.9 → 0.2.11
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 +95 -8
- 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,15 +141,23 @@ 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
|
}
|
|
152
|
+
var isFreeModel = (model) => typeof model === "string" && (/:free$/i.test(model.trim()) || /^openrouter\/free$/i.test(model.trim()));
|
|
153
|
+
var dynamicPrice = null;
|
|
154
|
+
var FREE_PRICE = { provider: "openrouter", tier: "fast", input: 0, output: 0, cachedInput: 0 };
|
|
140
155
|
function priceFor(model) {
|
|
156
|
+
if (isFreeModel(model)) return { key: String(model).trim().toLowerCase(), ...FREE_PRICE };
|
|
141
157
|
const key = normalizeModel(model);
|
|
158
|
+
if (key && PRICES[key]) return { key, ...PRICES[key] };
|
|
159
|
+
const dyn = typeof model === "string" && dynamicPrice ? dynamicPrice(model) : null;
|
|
160
|
+
if (dyn) return { key: String(model).trim().toLowerCase(), ...dyn };
|
|
142
161
|
return key ? { key, ...PRICES[key] } : null;
|
|
143
162
|
}
|
|
144
163
|
function costOf(model, u, factor = 1) {
|
|
@@ -338,7 +357,7 @@ function makeLocator(masked) {
|
|
|
338
357
|
};
|
|
339
358
|
return { text, starts, lineOf };
|
|
340
359
|
}
|
|
341
|
-
var ASSIGN = (name) => new RegExp(
|
|
360
|
+
var ASSIGN = (name) => new RegExp(`^[ \\t]*(?:export[ \\t]+)?(?:(?:const|let|var|final|static|private|public|readonly)[ \\t]+)*(?:self\\.|this\\.)?${name}[ \\t]*(?::[^=\\n]{0,200})?=(?![=>])`);
|
|
342
361
|
var KEYWORDS = /* @__PURE__ */ new Set(["self", "this", "await", "async", "return", "model", "messages", "role", "content", "system", "user", "true", "false", "True", "False", "None", "null", "undefined", "new", "max_tokens", "temperature", "client", "resp", "response"]);
|
|
343
362
|
function assignmentScope(loc, masked, line) {
|
|
344
363
|
const eq = masked[line].search(/=(?![=>])/);
|
|
@@ -407,6 +426,10 @@ var MODEL_NAME_TEST = /\b(?:model|model_?id|model_?name|modelId|modelName|deploy
|
|
|
407
426
|
var VOLATILE_CALL = /\b(?:datetime\.(?:now|utcnow)\(|date\.today\(|time\.time\(|time\.strftime\(|Date\.now\(|new Date\(\s*\)|\.toISOString\(|\.isoformat\(|\.toLocale(?:Date|Time)?String\(|uuid\.?uuid4\(|uuid4\(|randomUUID\(|nanoid\(|Math\.random\(|secrets\.token_hex\()/;
|
|
408
427
|
var INTERPOLATES = /\bf["']|\$\{|\.format\(|%\s*\(|["']\s*\+|\+\s*["']|`/;
|
|
409
428
|
var SYSTEM_CTX = /\bsystem(?:_?prompt|Prompt|_?message|Message|_?instructions?)?\b\s*[:=(]|["']role["']\s*:\s*["']system["']|\brole\s*:\s*["']system["']|\binstructions\s*[:=]/i;
|
|
429
|
+
var HARDCODED_KEY = /["'`]((?:sk-ant-api\d\d-|sk-ant-|sk-proj-|sk-or-v1-|sk-svcacct-)[A-Za-z0-9_-]{20,200}|sk-[A-Za-z0-9]{40,200}|AIza[0-9A-Za-z_-]{35}|xai-[A-Za-z0-9]{40,200}|gsk_[A-Za-z0-9]{40,200}|csk-[A-Za-z0-9]{30,200}|tz_live_[A-Za-z0-9_-]{24,200})["'`]/;
|
|
430
|
+
var KEY_PLACEHOLDER = /x{6,}|X{6,}|0{8,}|1234567|abcdef|your|example|placeholder|dummy|fake|test|redacted|\.\.\./i;
|
|
431
|
+
var keyProvider = (k) => k.startsWith("sk-ant-") ? "Anthropic" : k.startsWith("sk-or-") ? "OpenRouter" : k.startsWith("AIza") ? "Google" : k.startsWith("xai-") ? "xAI" : k.startsWith("gsk_") ? "Groq" : k.startsWith("csk-") ? "Cerebras" : k.startsWith("tz_live_") ? "TokenZero" : "OpenAI";
|
|
432
|
+
var PREFIX_DYNAMIC = /\bf(?:"""|'''|"|')\s{0,4}\{[A-Za-z_][\w.\[\]'"]{0,60}\}|`\s{0,4}\$\{[^}\n]{1,80}\}/;
|
|
410
433
|
var RANDOM_ID = /(?:\b(?:tool_call_id|toolCallId|call_id|callId|tool_use_id|toolUseId)\b|["']id["'])\s*[:=]\s*[^,\n]*?(?:uuid\.?uuid4\(|uuid4\(|randomUUID\(|nanoid\(|Math\.random\(|secrets\.token_hex\(|shortuuid)/;
|
|
411
434
|
var SET_TOOLS = /\btools?\s*=\s*\[[^\]\n]*\bfor\s+\w+\s+in\s+(?:set\(|\w*_?set\b|\w+\.keys\(\)\s*\|)/;
|
|
412
435
|
var OPENAI_COMPAT = /\bfrom\s+openai\s+import|\bimport\s+openai\b|from\s+["']openai["']|require\(\s*["']openai["']\s*\)|\bOpenAI\(|openrouter\.ai|chat\.completions\.create|litellm\.completion/;
|
|
@@ -418,6 +441,26 @@ var CACHE_USAGE_FIELD = /cache_read_input_tokens|cache_creation_input_tokens|cac
|
|
|
418
441
|
var COST_MATH = /\b(?:input_tokens|prompt_tokens|promptTokenCount|inputTokens|promptTokens|prompt_token_count)\b[^;\n]*(?:\*\s*[\w.[\]'"]*(?:price|rate|cost|per_?m|perM|usd)|\/\s*1[_,]?000[_,]?000|\/\s*1e6)|(?:price|rate|cost|per_?m|perM|usd)\w*\s*\*\s*[\w.[\]'"]*\b(?:input_tokens|prompt_tokens|promptTokenCount|inputTokens|promptTokens)\b/i;
|
|
419
442
|
var ZEROED_CACHE_USAGE = /\b(?:cache_creation_input_tokens|cache_read_input_tokens|cacheCreationInputTokens|cacheReadInputTokens|cached_tokens|cachedTokens)["']?\s*[:=]\s*0\b(?!\s*(?:if|\?|\|\||or\b|&&))/;
|
|
420
443
|
var isComment = (raw) => /^\s*(?:#|\/\/|\*|\/\*)/.test(raw);
|
|
444
|
+
function secretLint(path3, lines) {
|
|
445
|
+
const out = [];
|
|
446
|
+
for (let i = 0; i < lines.length && out.length < 5; i++) {
|
|
447
|
+
const raw = lines[i];
|
|
448
|
+
if (raw.length > 2e4) continue;
|
|
449
|
+
const km = HARDCODED_KEY.exec(raw);
|
|
450
|
+
if (!km || KEY_PLACEHOLDER.test(km[1])) continue;
|
|
451
|
+
const who = keyProvider(km[1]);
|
|
452
|
+
out.push({
|
|
453
|
+
file: path3,
|
|
454
|
+
severity: "high",
|
|
455
|
+
line: i + 1,
|
|
456
|
+
title: `Hard-coded ${who} API key in source`,
|
|
457
|
+
detail: `A string that looks like a live ${who} key (${km[1].slice(0, 7)}\u2026) is committed in this file. Anyone who can read the repository or its history can bill your account; a key committed to a public repo and never revoked is how the October 2026 LiteLLM supply-chain compromise began.`,
|
|
458
|
+
fix: "Revoke the key at the provider now (removing it from the file does not remove it from git history), then load it from the environment or a secret manager. TokenZero never needs your key in code: SDKs read OPENAI_API_KEY / ANTHROPIC_API_KEY as usual.",
|
|
459
|
+
est_savings_pct: 0
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
return out;
|
|
463
|
+
}
|
|
421
464
|
function cacheLint(path3, lines, masked) {
|
|
422
465
|
const out = [];
|
|
423
466
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -472,6 +515,15 @@ function cacheLint(path3, lines, masked) {
|
|
|
472
515
|
fix: "Keep the system prompt static. Put the current time or request id in the last user message (or round it to the day if it must stay in the system prompt).",
|
|
473
516
|
est_savings_pct: 15
|
|
474
517
|
});
|
|
518
|
+
const pm = PREFIX_DYNAMIC.exec(raw);
|
|
519
|
+
if (pm && SYSTEM_CTX.test(raw.slice(0, pm.index + 1)) && raw.length - pm.index >= 160) add("prefix-order", {
|
|
520
|
+
severity: "medium",
|
|
521
|
+
line: i + 1,
|
|
522
|
+
title: "Per-request value at the start of the system prompt (everything after it misses the cache)",
|
|
523
|
+
detail: "The system prompt opens with an interpolated value and continues with a long fixed block. Provider caches match byte-for-byte from the first token, so when the opening varies per request the fixed block behind it is re-billed at the full input rate every time. Moving per-job details below the shared prompt took a published AI code-review pipeline from $2.43 to $0.77 per run.",
|
|
524
|
+
fix: "Put the fixed instructions first and the per-request details (names, ids, dates, file lists) at the end, ideally in the last user message.",
|
|
525
|
+
est_savings_pct: 20
|
|
526
|
+
});
|
|
475
527
|
if (RANDOM_ID.test(raw) || SET_TOOLS.test(raw)) add("serialization", {
|
|
476
528
|
severity: "low",
|
|
477
529
|
line: i + 1,
|
|
@@ -678,6 +730,7 @@ function analyzeFiles(files, repo = null, manifests2 = []) {
|
|
|
678
730
|
findings.push(...local);
|
|
679
731
|
siteFindings.push(local);
|
|
680
732
|
});
|
|
733
|
+
findings.push(...secretLint(f.path, lines));
|
|
681
734
|
if (callLines.length || hasCacheControl || USAGE_FIELD.test(src)) {
|
|
682
735
|
for (const fd of cacheLint(f.path, lines, masked)) findings.push({ ...fd, severity: kind === "library" || illustrative ? DOWN[fd.severity] : fd.severity });
|
|
683
736
|
}
|
|
@@ -880,14 +933,39 @@ function manifests(root) {
|
|
|
880
933
|
}
|
|
881
934
|
|
|
882
935
|
// mcp/src/main.ts
|
|
883
|
-
var VERSION = "0.2.
|
|
936
|
+
var VERSION = "0.2.11";
|
|
884
937
|
var BAKED_URL = true ? "https://tokenzero.sh" : "__TOKENZERO_DEFAULT_URL__";
|
|
885
938
|
var SUPPORTED = [...MCP_LEGACY_VERSIONS];
|
|
886
939
|
var SERVER = { name: "tokenzero", version: VERSION };
|
|
887
|
-
var CAPABILITIES = { tools: { listChanged: false } };
|
|
888
|
-
var
|
|
940
|
+
var CAPABILITIES = { tools: { listChanged: false }, prompts: { listChanged: false } };
|
|
941
|
+
var PROMPTS = [
|
|
942
|
+
{ 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 }] },
|
|
943
|
+
{ 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 }] },
|
|
944
|
+
{ name: "try-tokenzero", title: "Try TokenZero on one call", description: "Run one priced call through TokenZero with no key or setup and show the saving and its signed receipt.", arguments: [] }
|
|
945
|
+
];
|
|
946
|
+
function promptMessages(name, args = {}) {
|
|
947
|
+
const p = PROMPTS.find((x) => x.name === name);
|
|
948
|
+
if (!p) throw new Error(`unknown prompt ${name}; available: ${PROMPTS.map((x) => x.name).join(", ")}`);
|
|
949
|
+
const say = (text) => ({ description: p.description, messages: [{ role: "user", content: { type: "text", text } }] });
|
|
950
|
+
const repo = typeof args?.repo === "string" && /^[\w.-]+\/[\w.-]+$/.test(args.repo.trim()) ? args.repo.trim() : "";
|
|
951
|
+
if (name === "cut-my-llm-bill") return say(`Use the TokenZero MCP tools to find where this project wastes LLM tokens. Call audit_repo_token_waste with ${repo ? `repo "${repo}"` : 'path "." (it scans this machine; nothing is uploaded)'}. Then list the top 3 findings with file:line, the estimated saving and the fix, and finish with the one-line change that applies the fixes automatically (base_url ${serviceUrl() || "https://api.tokenzero.sh"}/v1, my provider key stays mine). If nothing calls an LLM here, call try_tokenzero instead and show what it measured.`);
|
|
952
|
+
if (name === "price-my-workload") {
|
|
953
|
+
const model = typeof args?.model === "string" && args.model.trim() ? args.model.trim().slice(0, 80) : "gpt-6.1-sol";
|
|
954
|
+
const n = Number(args?.requests_per_month) > 0 ? Math.min(1e9, Math.round(Number(args.requests_per_month))) : 1e4;
|
|
955
|
+
return say(`Call the TokenZero tool calculate_model_arbitrage with {"model": "${model}", "requests_per_month": ${n}}. Show the monthly cost on ${model}, on its cheaper sibling, and with prompt caching, as a short table, then one sentence on which change saves the most.`);
|
|
956
|
+
}
|
|
957
|
+
return say("Call the TokenZero tool try_tokenzero with no arguments. Show what happened, the saving it measured, and the signed receipt id, then the keyless curl it returns so I can try a real call.");
|
|
958
|
+
}
|
|
959
|
+
var INSTRUCTIONS = "Start with try_tokenzero (no key, no setup). Use provision_workspace to create a TokenZero workspace and key with no form; audit_repo_token_waste to find LLM cost waste in the current project (local, nothing uploaded), calculate_model_arbitrage to price model alternatives, optimize_prompt_payload to make a request body cache-friendly.";
|
|
889
960
|
var serviceUrl = () => (process.env.TOKENZERO_URL || (BAKED_URL.startsWith("http") ? BAKED_URL : "")).replace(/\/+$/, "");
|
|
890
961
|
var TOOLS = [
|
|
962
|
+
{
|
|
963
|
+
name: "try_tokenzero",
|
|
964
|
+
title: "Try TokenZero (no key needed)",
|
|
965
|
+
annotations: { title: "Try TokenZero (no key needed)", readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
966
|
+
description: 'Call this first: no API key, no signup, no setup. Shows what TokenZero would do to one of your LLM calls: it runs the four savings engines (prompt-cache formatting, edge cache, cascade advice, signed receipt) on a sample or your own prompt and model, and returns the list-price saving, the signed receipt and a copy-paste keyless curl that makes a real call on a free model. Pass {} for the default, or {"model": "gpt-6.1-sol", "system": "...", "prompt": "..."}. Runs in the TokenZero service; send no secrets in the prompt.',
|
|
967
|
+
inputSchema: { type: "object", properties: { model: { type: "string", description: "a priced model id; default gpt-6.1-sol" }, system: { type: "string", description: "your system prompt (optional)" }, prompt: { type: "string", description: "your user message (optional)" } }, additionalProperties: false }
|
|
968
|
+
},
|
|
891
969
|
{
|
|
892
970
|
name: "audit_repo_token_waste",
|
|
893
971
|
title: "Audit a repo or directory for LLM token waste",
|
|
@@ -899,7 +977,7 @@ var TOOLS = [
|
|
|
899
977
|
name: "calculate_model_arbitrage",
|
|
900
978
|
title: "Price a workload across models",
|
|
901
979
|
annotations: { title: "Price a workload across models", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
902
|
-
description: `No API key needed; only \`model\` is required (defaults 2,000 tokens in and 400 out). Price a workload at list prices
|
|
980
|
+
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.`,
|
|
903
981
|
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 }
|
|
904
982
|
},
|
|
905
983
|
{
|
|
@@ -938,6 +1016,7 @@ async function callTool(name, args) {
|
|
|
938
1016
|
}
|
|
939
1017
|
case "calculate_model_arbitrage":
|
|
940
1018
|
return modelArbitrage(args);
|
|
1019
|
+
case "try_tokenzero":
|
|
941
1020
|
case "optimize_prompt_payload": {
|
|
942
1021
|
const base = serviceUrl();
|
|
943
1022
|
if (!base) throw new Error("set TOKENZERO_URL to use this tool (the cache formatter runs in the TokenZero service)");
|
|
@@ -1019,6 +1098,10 @@ async function handle(m) {
|
|
|
1019
1098
|
return reply(discoverResult(SERVER, CAPABILITIES, INSTRUCTIONS));
|
|
1020
1099
|
case "tools/list":
|
|
1021
1100
|
return reply(modernResult({ tools: TOOLS }, SERVER, true));
|
|
1101
|
+
case "prompts/list":
|
|
1102
|
+
return reply(modernResult({ prompts: PROMPTS }, SERVER, true));
|
|
1103
|
+
case "prompts/get":
|
|
1104
|
+
return reply(modernResult(promptMessages(String(m.params?.name ?? ""), m.params?.arguments), SERVER));
|
|
1022
1105
|
case "tools/call":
|
|
1023
1106
|
return reply(modernResult(await call(), SERVER));
|
|
1024
1107
|
default:
|
|
@@ -1038,6 +1121,10 @@ async function handle(m) {
|
|
|
1038
1121
|
return reply(discoverResult(SERVER, CAPABILITIES, INSTRUCTIONS));
|
|
1039
1122
|
case "tools/list":
|
|
1040
1123
|
return reply({ tools: TOOLS });
|
|
1124
|
+
case "prompts/list":
|
|
1125
|
+
return reply({ prompts: PROMPTS });
|
|
1126
|
+
case "prompts/get":
|
|
1127
|
+
return reply(promptMessages(String(m.params?.name ?? ""), m.params?.arguments));
|
|
1041
1128
|
case "tools/call":
|
|
1042
1129
|
return reply(await call());
|
|
1043
1130
|
default:
|
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.11",
|
|
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",
|