@yawlabs/tokenmeter-mcp 0.1.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/dist/index.d.ts +17 -0
- package/dist/index.js +140 -0
- package/package.json +29 -0
- package/src/index.ts +299 -0
- package/tsconfig.json +14 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Token Meter MCP Client
|
|
4
|
+
*
|
|
5
|
+
* This is the ONLY code distributed to users.
|
|
6
|
+
* It is a thin API client — all business logic runs on tokenmeter.sh.
|
|
7
|
+
*
|
|
8
|
+
* The client:
|
|
9
|
+
* 1. Connects to the user's MCP host (Claude, Cursor, Yaw, etc.)
|
|
10
|
+
* 2. Registers tools by fetching the tool list from the API
|
|
11
|
+
* 3. When a tool is called, proxies the request to api.tokenmeter.sh
|
|
12
|
+
* 4. Returns the response to the MCP host
|
|
13
|
+
*
|
|
14
|
+
* No provider translation, routing, cost calculation, or pricing data
|
|
15
|
+
* is included in this package.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Token Meter MCP Client
|
|
4
|
+
*
|
|
5
|
+
* This is the ONLY code distributed to users.
|
|
6
|
+
* It is a thin API client — all business logic runs on tokenmeter.sh.
|
|
7
|
+
*
|
|
8
|
+
* The client:
|
|
9
|
+
* 1. Connects to the user's MCP host (Claude, Cursor, Yaw, etc.)
|
|
10
|
+
* 2. Registers tools by fetching the tool list from the API
|
|
11
|
+
* 3. When a tool is called, proxies the request to api.tokenmeter.sh
|
|
12
|
+
* 4. Returns the response to the MCP host
|
|
13
|
+
*
|
|
14
|
+
* No provider translation, routing, cost calculation, or pricing data
|
|
15
|
+
* is included in this package.
|
|
16
|
+
*/
|
|
17
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
18
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
const API_BASE = process.env.TOKENMETER_API_URL ?? "https://api.tokenmeter.sh";
|
|
21
|
+
const API_KEY = process.env.TOKENMETER_API_KEY ?? "";
|
|
22
|
+
async function apiCall(endpoint, params = {}) {
|
|
23
|
+
const res = await fetch(`${API_BASE}${endpoint}`, {
|
|
24
|
+
method: "POST",
|
|
25
|
+
headers: {
|
|
26
|
+
"content-type": "application/json",
|
|
27
|
+
...(API_KEY ? { authorization: `Bearer ${API_KEY}` } : {}),
|
|
28
|
+
},
|
|
29
|
+
body: JSON.stringify(params),
|
|
30
|
+
});
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
const error = await res.text().catch(() => "Unknown error");
|
|
33
|
+
throw new Error(`Token Meter API error (${res.status}): ${error}`);
|
|
34
|
+
}
|
|
35
|
+
return res.json();
|
|
36
|
+
}
|
|
37
|
+
function text(data) {
|
|
38
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
39
|
+
}
|
|
40
|
+
async function main() {
|
|
41
|
+
if (!API_KEY) {
|
|
42
|
+
console.error("TOKENMETER_API_KEY is required. Get your key at https://tokenmeter.sh/settings");
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
const server = new McpServer({ name: "tokenmeter", version: "0.1.0" });
|
|
46
|
+
// ─── Free Tier ───────────────────────────────────────────
|
|
47
|
+
server.tool("get_spend_summary", "Get total LLM spend for today, this week, and this month, broken down by provider.", {}, async () => text(await apiCall("/v1/tools/get_spend_summary")));
|
|
48
|
+
server.tool("get_session_cost", "Get the cost of the current conversation/session.", {}, async () => text(await apiCall("/v1/tools/get_session_cost")));
|
|
49
|
+
server.tool("get_model_pricing", "Get current per-token pricing for any model or provider.", {
|
|
50
|
+
model: z.string().optional().describe("Model ID"),
|
|
51
|
+
provider: z.string().optional().describe("Provider name"),
|
|
52
|
+
}, async (params) => text(await apiCall("/v1/tools/get_model_pricing", params)));
|
|
53
|
+
server.tool("get_budget_status", "Check current spend against configured budget limits.", {}, async () => text(await apiCall("/v1/tools/get_budget_status")));
|
|
54
|
+
server.tool("get_providers", "List connected LLM providers and their status.", {}, async () => text(await apiCall("/v1/tools/get_providers")));
|
|
55
|
+
// ─── Pro Tier ────────────────────────────────────────────
|
|
56
|
+
server.tool("get_spend_breakdown", "Detailed spend breakdown with filters by provider, model, project, and date range.", {
|
|
57
|
+
provider: z.string().optional(),
|
|
58
|
+
days: z.number().optional().default(7),
|
|
59
|
+
}, async (params) => text(await apiCall("/v1/tools/get_spend_breakdown", params)));
|
|
60
|
+
server.tool("get_cost_trend", "Show daily spend over time to identify trends.", {
|
|
61
|
+
days: z.number().optional().default(14),
|
|
62
|
+
}, async (params) => text(await apiCall("/v1/tools/get_cost_trend", params)));
|
|
63
|
+
server.tool("get_top_models", "Rank models by spend or token volume.", {
|
|
64
|
+
sortBy: z.enum(["cost", "tokens"]).optional().default("cost"),
|
|
65
|
+
days: z.number().optional().default(30),
|
|
66
|
+
}, async (params) => text(await apiCall("/v1/tools/get_top_models", params)));
|
|
67
|
+
server.tool("get_cost_estimate", "Estimate cost for a given number of tokens on specified models.", {
|
|
68
|
+
models: z.array(z.string()),
|
|
69
|
+
inputTokens: z.number(),
|
|
70
|
+
outputTokens: z.number().optional().default(500),
|
|
71
|
+
useCache: z.boolean().optional().default(false),
|
|
72
|
+
}, async (params) => text(await apiCall("/v1/tools/get_cost_estimate", params)));
|
|
73
|
+
server.tool("compare_models", "Compare models on cost for a specific workload.", {
|
|
74
|
+
inputTokens: z.number(),
|
|
75
|
+
outputTokens: z.number(),
|
|
76
|
+
callsPerDay: z.number().optional().default(100),
|
|
77
|
+
provider: z.string().optional(),
|
|
78
|
+
}, async (params) => text(await apiCall("/v1/tools/compare_models", params)));
|
|
79
|
+
server.tool("get_anomalies", "Flag unusual spend spikes compared to historical average.", {}, async () => text(await apiCall("/v1/tools/get_anomalies")));
|
|
80
|
+
server.tool("set_budget_alert", "Create a spend threshold notification or hard cap.", {
|
|
81
|
+
name: z.string(),
|
|
82
|
+
limit: z.number(),
|
|
83
|
+
period: z.enum(["daily", "weekly", "monthly"]),
|
|
84
|
+
hardCap: z.boolean().optional().default(false),
|
|
85
|
+
}, async (params) => text(await apiCall("/v1/tools/set_budget_alert", params)));
|
|
86
|
+
server.tool("set_budget_cap", "Set a hard spending cap that blocks requests when hit.", {
|
|
87
|
+
limit: z.number(),
|
|
88
|
+
period: z.enum(["daily", "weekly", "monthly"]),
|
|
89
|
+
}, async (params) => text(await apiCall("/v1/tools/set_budget_cap", params)));
|
|
90
|
+
server.tool("tag_session", "Label the current session with a project name for cost attribution.", {
|
|
91
|
+
project: z.string(),
|
|
92
|
+
}, async (params) => text(await apiCall("/v1/tools/tag_session", params)));
|
|
93
|
+
server.tool("export_report", "Generate a spend report for a date range.", {
|
|
94
|
+
days: z.number().optional().default(30),
|
|
95
|
+
format: z.enum(["json", "csv"]).optional().default("json"),
|
|
96
|
+
}, async (params) => text(await apiCall("/v1/tools/export_report", params)));
|
|
97
|
+
// ─── Gateway Tier ────────────────────────────────────────
|
|
98
|
+
server.tool("get_rate_limits", "Show current rate limit status per provider and model.", {}, async () => text(await apiCall("/v1/tools/get_rate_limits")));
|
|
99
|
+
server.tool("get_routing_status", "Show which providers are healthy, degraded, or rate-limited.", {}, async () => text(await apiCall("/v1/tools/get_routing_status")));
|
|
100
|
+
server.tool("set_routing_rule", "Configure routing preferences (cheapest, fastest, specific provider).", {
|
|
101
|
+
name: z.string(),
|
|
102
|
+
strategy: z.enum(["cheapest", "fastest", "specific"]),
|
|
103
|
+
provider: z.string().optional(),
|
|
104
|
+
model: z.string().optional(),
|
|
105
|
+
}, async (params) => text(await apiCall("/v1/tools/set_routing_rule", params)));
|
|
106
|
+
server.tool("set_fallback_chain", "Define provider failover order.", {
|
|
107
|
+
chain: z.array(z.object({
|
|
108
|
+
provider: z.string(),
|
|
109
|
+
model: z.string(),
|
|
110
|
+
})),
|
|
111
|
+
}, async (params) => text(await apiCall("/v1/tools/set_fallback_chain", params)));
|
|
112
|
+
server.tool("set_project_quota", "Set a hard spend cap per project.", {
|
|
113
|
+
project: z.string(),
|
|
114
|
+
limit: z.number(),
|
|
115
|
+
period: z.enum(["daily", "weekly", "monthly"]),
|
|
116
|
+
}, async (params) => text(await apiCall("/v1/tools/set_project_quota", params)));
|
|
117
|
+
server.tool("get_latency_report", "Show P50/P95/P99 latency per provider and model.", {
|
|
118
|
+
days: z.number().optional().default(7),
|
|
119
|
+
}, async (params) => text(await apiCall("/v1/tools/get_latency_report", params)));
|
|
120
|
+
server.tool("set_model_alias", "Map a friendly name to a routing rule.", {
|
|
121
|
+
alias: z.string(),
|
|
122
|
+
provider: z.string(),
|
|
123
|
+
model: z.string(),
|
|
124
|
+
}, async (params) => text(await apiCall("/v1/tools/set_model_alias", params)));
|
|
125
|
+
// ─── Team Tier ───────────────────────────────────────────
|
|
126
|
+
server.tool("get_team_spend", "Get aggregated spend across all team members.", {
|
|
127
|
+
days: z.number().optional().default(30),
|
|
128
|
+
}, async (params) => text(await apiCall("/v1/tools/get_team_spend", params)));
|
|
129
|
+
server.tool("set_team_budget", "Set an organization-level budget cap.", {
|
|
130
|
+
limit: z.number(),
|
|
131
|
+
period: z.enum(["daily", "weekly", "monthly"]),
|
|
132
|
+
}, async (params) => text(await apiCall("/v1/tools/set_team_budget", params)));
|
|
133
|
+
// Connect
|
|
134
|
+
const transport = new StdioServerTransport();
|
|
135
|
+
await server.connect(transport);
|
|
136
|
+
}
|
|
137
|
+
main().catch((error) => {
|
|
138
|
+
console.error("Fatal error:", error);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yawlabs/tokenmeter-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "Token Meter MCP client — LLM spend tracking, smart routing, and rate limit management",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"bin": {
|
|
11
|
+
"tokenmeter-mcp": "dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"start": "node dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": ["mcp", "llm", "tokens", "cost", "pricing", "ai"],
|
|
18
|
+
"author": "Yaw Labs",
|
|
19
|
+
"license": "UNLICENSED",
|
|
20
|
+
"homepage": "https://tokenmeter.sh",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
23
|
+
"zod": "^3.24.4"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"typescript": "^5.7.0",
|
|
27
|
+
"@types/node": "^22.0.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Token Meter MCP Client
|
|
5
|
+
*
|
|
6
|
+
* This is the ONLY code distributed to users.
|
|
7
|
+
* It is a thin API client — all business logic runs on tokenmeter.sh.
|
|
8
|
+
*
|
|
9
|
+
* The client:
|
|
10
|
+
* 1. Connects to the user's MCP host (Claude, Cursor, Yaw, etc.)
|
|
11
|
+
* 2. Registers tools by fetching the tool list from the API
|
|
12
|
+
* 3. When a tool is called, proxies the request to api.tokenmeter.sh
|
|
13
|
+
* 4. Returns the response to the MCP host
|
|
14
|
+
*
|
|
15
|
+
* No provider translation, routing, cost calculation, or pricing data
|
|
16
|
+
* is included in this package.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
20
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
|
|
23
|
+
const API_BASE = process.env.TOKENMETER_API_URL ?? "https://api.tokenmeter.sh";
|
|
24
|
+
const API_KEY = process.env.TOKENMETER_API_KEY ?? "";
|
|
25
|
+
|
|
26
|
+
async function apiCall(endpoint: string, params: Record<string, unknown> = {}): Promise<unknown> {
|
|
27
|
+
const res = await fetch(`${API_BASE}${endpoint}`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: {
|
|
30
|
+
"content-type": "application/json",
|
|
31
|
+
...(API_KEY ? { authorization: `Bearer ${API_KEY}` } : {}),
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify(params),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!res.ok) {
|
|
37
|
+
const error = await res.text().catch(() => "Unknown error");
|
|
38
|
+
throw new Error(`Token Meter API error (${res.status}): ${error}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return res.json();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function text(data: unknown) {
|
|
45
|
+
return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function main() {
|
|
49
|
+
if (!API_KEY) {
|
|
50
|
+
console.error("TOKENMETER_API_KEY is required. Get your key at https://tokenmeter.sh/settings");
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const server = new McpServer({ name: "tokenmeter", version: "0.1.0" });
|
|
55
|
+
|
|
56
|
+
// ─── Free Tier ───────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
server.tool(
|
|
59
|
+
"get_spend_summary",
|
|
60
|
+
"Get total LLM spend for today, this week, and this month, broken down by provider.",
|
|
61
|
+
{},
|
|
62
|
+
async () => text(await apiCall("/v1/tools/get_spend_summary"))
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
server.tool(
|
|
66
|
+
"get_session_cost",
|
|
67
|
+
"Get the cost of the current conversation/session.",
|
|
68
|
+
{},
|
|
69
|
+
async () => text(await apiCall("/v1/tools/get_session_cost"))
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
server.tool(
|
|
73
|
+
"get_model_pricing",
|
|
74
|
+
"Get current per-token pricing for any model or provider.",
|
|
75
|
+
{
|
|
76
|
+
model: z.string().optional().describe("Model ID"),
|
|
77
|
+
provider: z.string().optional().describe("Provider name"),
|
|
78
|
+
},
|
|
79
|
+
async (params) => text(await apiCall("/v1/tools/get_model_pricing", params))
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
server.tool(
|
|
83
|
+
"get_budget_status",
|
|
84
|
+
"Check current spend against configured budget limits.",
|
|
85
|
+
{},
|
|
86
|
+
async () => text(await apiCall("/v1/tools/get_budget_status"))
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
server.tool(
|
|
90
|
+
"get_providers",
|
|
91
|
+
"List connected LLM providers and their status.",
|
|
92
|
+
{},
|
|
93
|
+
async () => text(await apiCall("/v1/tools/get_providers"))
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// ─── Pro Tier ────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
server.tool(
|
|
99
|
+
"get_spend_breakdown",
|
|
100
|
+
"Detailed spend breakdown with filters by provider, model, project, and date range.",
|
|
101
|
+
{
|
|
102
|
+
provider: z.string().optional(),
|
|
103
|
+
days: z.number().optional().default(7),
|
|
104
|
+
},
|
|
105
|
+
async (params) => text(await apiCall("/v1/tools/get_spend_breakdown", params))
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
server.tool(
|
|
109
|
+
"get_cost_trend",
|
|
110
|
+
"Show daily spend over time to identify trends.",
|
|
111
|
+
{
|
|
112
|
+
days: z.number().optional().default(14),
|
|
113
|
+
},
|
|
114
|
+
async (params) => text(await apiCall("/v1/tools/get_cost_trend", params))
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
server.tool(
|
|
118
|
+
"get_top_models",
|
|
119
|
+
"Rank models by spend or token volume.",
|
|
120
|
+
{
|
|
121
|
+
sortBy: z.enum(["cost", "tokens"]).optional().default("cost"),
|
|
122
|
+
days: z.number().optional().default(30),
|
|
123
|
+
},
|
|
124
|
+
async (params) => text(await apiCall("/v1/tools/get_top_models", params))
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
server.tool(
|
|
128
|
+
"get_cost_estimate",
|
|
129
|
+
"Estimate cost for a given number of tokens on specified models.",
|
|
130
|
+
{
|
|
131
|
+
models: z.array(z.string()),
|
|
132
|
+
inputTokens: z.number(),
|
|
133
|
+
outputTokens: z.number().optional().default(500),
|
|
134
|
+
useCache: z.boolean().optional().default(false),
|
|
135
|
+
},
|
|
136
|
+
async (params) => text(await apiCall("/v1/tools/get_cost_estimate", params))
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
server.tool(
|
|
140
|
+
"compare_models",
|
|
141
|
+
"Compare models on cost for a specific workload.",
|
|
142
|
+
{
|
|
143
|
+
inputTokens: z.number(),
|
|
144
|
+
outputTokens: z.number(),
|
|
145
|
+
callsPerDay: z.number().optional().default(100),
|
|
146
|
+
provider: z.string().optional(),
|
|
147
|
+
},
|
|
148
|
+
async (params) => text(await apiCall("/v1/tools/compare_models", params))
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
server.tool(
|
|
152
|
+
"get_anomalies",
|
|
153
|
+
"Flag unusual spend spikes compared to historical average.",
|
|
154
|
+
{},
|
|
155
|
+
async () => text(await apiCall("/v1/tools/get_anomalies"))
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
server.tool(
|
|
159
|
+
"set_budget_alert",
|
|
160
|
+
"Create a spend threshold notification or hard cap.",
|
|
161
|
+
{
|
|
162
|
+
name: z.string(),
|
|
163
|
+
limit: z.number(),
|
|
164
|
+
period: z.enum(["daily", "weekly", "monthly"]),
|
|
165
|
+
hardCap: z.boolean().optional().default(false),
|
|
166
|
+
},
|
|
167
|
+
async (params) => text(await apiCall("/v1/tools/set_budget_alert", params))
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
server.tool(
|
|
171
|
+
"set_budget_cap",
|
|
172
|
+
"Set a hard spending cap that blocks requests when hit.",
|
|
173
|
+
{
|
|
174
|
+
limit: z.number(),
|
|
175
|
+
period: z.enum(["daily", "weekly", "monthly"]),
|
|
176
|
+
},
|
|
177
|
+
async (params) => text(await apiCall("/v1/tools/set_budget_cap", params))
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
server.tool(
|
|
181
|
+
"tag_session",
|
|
182
|
+
"Label the current session with a project name for cost attribution.",
|
|
183
|
+
{
|
|
184
|
+
project: z.string(),
|
|
185
|
+
},
|
|
186
|
+
async (params) => text(await apiCall("/v1/tools/tag_session", params))
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
server.tool(
|
|
190
|
+
"export_report",
|
|
191
|
+
"Generate a spend report for a date range.",
|
|
192
|
+
{
|
|
193
|
+
days: z.number().optional().default(30),
|
|
194
|
+
format: z.enum(["json", "csv"]).optional().default("json"),
|
|
195
|
+
},
|
|
196
|
+
async (params) => text(await apiCall("/v1/tools/export_report", params))
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// ─── Gateway Tier ────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
server.tool(
|
|
202
|
+
"get_rate_limits",
|
|
203
|
+
"Show current rate limit status per provider and model.",
|
|
204
|
+
{},
|
|
205
|
+
async () => text(await apiCall("/v1/tools/get_rate_limits"))
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
server.tool(
|
|
209
|
+
"get_routing_status",
|
|
210
|
+
"Show which providers are healthy, degraded, or rate-limited.",
|
|
211
|
+
{},
|
|
212
|
+
async () => text(await apiCall("/v1/tools/get_routing_status"))
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
server.tool(
|
|
216
|
+
"set_routing_rule",
|
|
217
|
+
"Configure routing preferences (cheapest, fastest, specific provider).",
|
|
218
|
+
{
|
|
219
|
+
name: z.string(),
|
|
220
|
+
strategy: z.enum(["cheapest", "fastest", "specific"]),
|
|
221
|
+
provider: z.string().optional(),
|
|
222
|
+
model: z.string().optional(),
|
|
223
|
+
},
|
|
224
|
+
async (params) => text(await apiCall("/v1/tools/set_routing_rule", params))
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
server.tool(
|
|
228
|
+
"set_fallback_chain",
|
|
229
|
+
"Define provider failover order.",
|
|
230
|
+
{
|
|
231
|
+
chain: z.array(z.object({
|
|
232
|
+
provider: z.string(),
|
|
233
|
+
model: z.string(),
|
|
234
|
+
})),
|
|
235
|
+
},
|
|
236
|
+
async (params) => text(await apiCall("/v1/tools/set_fallback_chain", params))
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
server.tool(
|
|
240
|
+
"set_project_quota",
|
|
241
|
+
"Set a hard spend cap per project.",
|
|
242
|
+
{
|
|
243
|
+
project: z.string(),
|
|
244
|
+
limit: z.number(),
|
|
245
|
+
period: z.enum(["daily", "weekly", "monthly"]),
|
|
246
|
+
},
|
|
247
|
+
async (params) => text(await apiCall("/v1/tools/set_project_quota", params))
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
server.tool(
|
|
251
|
+
"get_latency_report",
|
|
252
|
+
"Show P50/P95/P99 latency per provider and model.",
|
|
253
|
+
{
|
|
254
|
+
days: z.number().optional().default(7),
|
|
255
|
+
},
|
|
256
|
+
async (params) => text(await apiCall("/v1/tools/get_latency_report", params))
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
server.tool(
|
|
260
|
+
"set_model_alias",
|
|
261
|
+
"Map a friendly name to a routing rule.",
|
|
262
|
+
{
|
|
263
|
+
alias: z.string(),
|
|
264
|
+
provider: z.string(),
|
|
265
|
+
model: z.string(),
|
|
266
|
+
},
|
|
267
|
+
async (params) => text(await apiCall("/v1/tools/set_model_alias", params))
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
// ─── Team Tier ───────────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
server.tool(
|
|
273
|
+
"get_team_spend",
|
|
274
|
+
"Get aggregated spend across all team members.",
|
|
275
|
+
{
|
|
276
|
+
days: z.number().optional().default(30),
|
|
277
|
+
},
|
|
278
|
+
async (params) => text(await apiCall("/v1/tools/get_team_spend", params))
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
server.tool(
|
|
282
|
+
"set_team_budget",
|
|
283
|
+
"Set an organization-level budget cap.",
|
|
284
|
+
{
|
|
285
|
+
limit: z.number(),
|
|
286
|
+
period: z.enum(["daily", "weekly", "monthly"]),
|
|
287
|
+
},
|
|
288
|
+
async (params) => text(await apiCall("/v1/tools/set_team_budget", params))
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// Connect
|
|
292
|
+
const transport = new StdioServerTransport();
|
|
293
|
+
await server.connect(transport);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
main().catch((error) => {
|
|
297
|
+
console.error("Fatal error:", error);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"skipLibCheck": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"]
|
|
14
|
+
}
|