@tokenzero/mcp 0.2.11 → 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 +74 -3
- package/package.json +1 -1
package/bin/tokenzero-mcp.mjs
CHANGED
|
@@ -932,12 +932,58 @@ function manifests(root) {
|
|
|
932
932
|
});
|
|
933
933
|
}
|
|
934
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
|
+
|
|
935
970
|
// mcp/src/main.ts
|
|
936
|
-
var VERSION = "0.2.
|
|
971
|
+
var VERSION = "0.2.12";
|
|
937
972
|
var BAKED_URL = true ? "https://tokenzero.sh" : "__TOKENZERO_DEFAULT_URL__";
|
|
938
973
|
var SUPPORTED = [...MCP_LEGACY_VERSIONS];
|
|
939
974
|
var SERVER = { name: "tokenzero", version: VERSION };
|
|
940
|
-
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
|
+
}
|
|
941
987
|
var PROMPTS = [
|
|
942
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 }] },
|
|
943
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 }] },
|
|
@@ -1104,6 +1150,16 @@ async function handle(m) {
|
|
|
1104
1150
|
return reply(modernResult(promptMessages(String(m.params?.name ?? ""), m.params?.arguments), SERVER));
|
|
1105
1151
|
case "tools/call":
|
|
1106
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"] });
|
|
1107
1163
|
default:
|
|
1108
1164
|
if (typeof m.method === "string" && m.method.startsWith("notifications/")) return;
|
|
1109
1165
|
return fail(RPC.methodNotFound, `method not found: ${m.method}`);
|
|
@@ -1112,7 +1168,7 @@ async function handle(m) {
|
|
|
1112
1168
|
switch (m?.method) {
|
|
1113
1169
|
case "initialize": {
|
|
1114
1170
|
const want = m.params?.protocolVersion;
|
|
1115
|
-
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 });
|
|
1116
1172
|
}
|
|
1117
1173
|
case "ping":
|
|
1118
1174
|
return reply({});
|
|
@@ -1127,6 +1183,21 @@ async function handle(m) {
|
|
|
1127
1183
|
return reply(promptMessages(String(m.params?.name ?? ""), m.params?.arguments));
|
|
1128
1184
|
case "tools/call":
|
|
1129
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"] });
|
|
1130
1201
|
default:
|
|
1131
1202
|
if (typeof m?.method === "string" && m.method.startsWith("notifications/")) return;
|
|
1132
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",
|