vantageaiops-mcp 1.0.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.
Files changed (3) hide show
  1. package/README.md +183 -0
  2. package/dist/index.js +335 -0
  3. package/package.json +29 -0
package/README.md ADDED
@@ -0,0 +1,183 @@
1
+ # vantageaiops-mcp
2
+
3
+ MCP server for [VantageAI](https://vantageaiops.com) — track LLM costs and query analytics directly from your AI coding assistant.
4
+
5
+ [![npm](https://img.shields.io/npm/v/vantageaiops-mcp)](https://www.npmjs.com/package/vantageaiops-mcp)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Tools
9
+
10
+ | Tool | Description |
11
+ |------|-------------|
12
+ | `track_llm_call` | Log an LLM call (tokens, cost, latency, model, team) |
13
+ | `get_summary` | MTD spend, requests, top model, budget status |
14
+ | `get_kpis` | Full KPI table with deltas |
15
+ | `get_model_breakdown` | Cost + usage per model |
16
+ | `get_team_breakdown` | Cost + usage per team (chargeback) |
17
+ | `check_budget` | Budget % used, remaining, status |
18
+ | `get_traces` | Recent multi-step agent traces |
19
+ | `get_cost_gate` | CI/CD gate — pass/fail vs budget |
20
+
21
+ ## Setup
22
+
23
+ ### 1. Get your API key
24
+
25
+ Sign up free at [vantageaiops.com/signup.html](https://vantageaiops.com/signup.html) — your `vnt_...` key is generated instantly.
26
+
27
+ ### 2. Add to your coding assistant
28
+
29
+ Pick your platform. All configs use `npx vantageaiops-mcp` — no local install needed.
30
+
31
+ ---
32
+
33
+ ## Claude Desktop
34
+
35
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "vantage": {
41
+ "command": "npx",
42
+ "args": ["-y", "vantageaiops-mcp"],
43
+ "env": {
44
+ "VANTAGE_API_KEY": "vnt_your_key_here"
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ Restart Claude Desktop. VantageAI tools appear in the tool picker automatically.
52
+
53
+ ---
54
+
55
+ ## Cursor
56
+
57
+ Edit `~/.cursor/mcp.json` (or Cursor → Settings → MCP):
58
+
59
+ ```json
60
+ {
61
+ "mcpServers": {
62
+ "vantage": {
63
+ "command": "npx",
64
+ "args": ["-y", "vantageaiops-mcp"],
65
+ "env": {
66
+ "VANTAGE_API_KEY": "vnt_your_key_here"
67
+ }
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Windsurf
76
+
77
+ Edit `~/.codeium/windsurf/mcp_config.json`:
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "vantage": {
83
+ "command": "npx",
84
+ "args": ["-y", "vantageaiops-mcp"],
85
+ "env": {
86
+ "VANTAGE_API_KEY": "vnt_your_key_here"
87
+ }
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ ---
94
+
95
+ ## VS Code (Copilot / GitHub Copilot Chat)
96
+
97
+ Add to `.vscode/mcp.json` in your project root (VS Code 1.99+):
98
+
99
+ ```json
100
+ {
101
+ "servers": {
102
+ "vantage": {
103
+ "type": "stdio",
104
+ "command": "npx",
105
+ "args": ["-y", "vantageaiops-mcp"],
106
+ "env": {
107
+ "VANTAGE_API_KEY": "vnt_your_key_here"
108
+ }
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Cline (VS Code extension)
117
+
118
+ Open Cline → MCP Servers → Add Server → paste:
119
+
120
+ ```json
121
+ {
122
+ "vantage": {
123
+ "command": "npx",
124
+ "args": ["-y", "vantageaiops-mcp"],
125
+ "env": {
126
+ "VANTAGE_API_KEY": "vnt_your_key_here"
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Zed
135
+
136
+ Add to your Zed `settings.json`:
137
+
138
+ ```json
139
+ {
140
+ "context_servers": {
141
+ "vantage": {
142
+ "command": {
143
+ "path": "npx",
144
+ "args": ["-y", "vantageaiops-mcp"],
145
+ "env": {
146
+ "VANTAGE_API_KEY": "vnt_your_key_here"
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Environment variables
157
+
158
+ | Variable | Required | Default | Description |
159
+ |----------|----------|---------|-------------|
160
+ | `VANTAGE_API_KEY` | ✅ | — | Your `vnt_...` API key from [signup](https://vantageaiops.com/signup.html) |
161
+ | `VANTAGE_ORG` | No | parsed from key | Org ID override |
162
+ | `VANTAGE_API_BASE` | No | `https://api.vantageaiops.com` | Custom API base URL |
163
+
164
+ ---
165
+
166
+ ## Example usage in chat
167
+
168
+ Once connected, ask your assistant:
169
+
170
+ - *"How much have we spent on LLMs this month?"* → `get_summary`
171
+ - *"Which model is costing us the most?"* → `get_model_breakdown`
172
+ - *"Are we within our AI budget?"* → `check_budget`
173
+ - *"Show me recent agent traces"* → `get_traces`
174
+ - *"Track this call: gpt-4o, 500 prompt tokens, 120 completion tokens, $0.003"* → `track_llm_call`
175
+
176
+ ---
177
+
178
+ ## Links
179
+
180
+ - [Dashboard](https://vantageaiops.com/app.html)
181
+ - [Docs](https://vantageaiops.com/docs.html)
182
+ - [Python SDK](https://pypi.org/project/vantageaiops/)
183
+ - [JavaScript SDK](https://www.npmjs.com/package/vantageaiops)
package/dist/index.js ADDED
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * VantageAI MCP Server
5
+ *
6
+ * Exposes VantageAI as an MCP server so AI coding assistants
7
+ * (Claude Desktop, Cursor, Windsurf, VS Code Copilot, Cline, etc.)
8
+ * can track LLM costs and query analytics in real-time.
9
+ *
10
+ * Config:
11
+ * VANTAGE_API_KEY — your vnt_... key (required)
12
+ * VANTAGE_ORG — org id (auto-parsed from key if omitted)
13
+ * VANTAGE_API_BASE — default: https://api.vantageaiops.com
14
+ *
15
+ * Tools:
16
+ * track_llm_call — ingest a single LLM event
17
+ * get_summary — current spend, requests, top model
18
+ * get_kpis — full KPI table
19
+ * get_model_breakdown — cost + usage per model
20
+ * get_team_breakdown — cost + usage per team
21
+ * check_budget — budget status + % used
22
+ * get_traces — recent agent traces
23
+ * get_cost_gate — CI/CD budget gate check
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
27
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
28
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
29
+ // ── Config ────────────────────────────────────────────────────────────────────
30
+ const API_KEY = process.env.VANTAGE_API_KEY ?? '';
31
+ const API_BASE = (process.env.VANTAGE_API_BASE ?? 'https://api.vantageaiops.com').replace(/\/+$/, '');
32
+ const ORG = process.env.VANTAGE_ORG ?? parseOrgFromKey(API_KEY);
33
+ function parseOrgFromKey(key) {
34
+ const parts = key.split('_');
35
+ return parts.length >= 3 ? parts[1] : 'default';
36
+ }
37
+ // ── API client ────────────────────────────────────────────────────────────────
38
+ async function api(path, opts = {}) {
39
+ if (!API_KEY)
40
+ throw new Error('VANTAGE_API_KEY is not set. Add it to your MCP config.');
41
+ const res = await fetch(`${API_BASE}${path}`, {
42
+ ...opts,
43
+ headers: {
44
+ 'Authorization': `Bearer ${API_KEY}`,
45
+ 'X-Vantage-Org': ORG,
46
+ 'Content-Type': 'application/json',
47
+ ...(opts.headers ?? {}),
48
+ },
49
+ });
50
+ const body = await res.json();
51
+ if (!res.ok)
52
+ throw new Error(body.error ?? `HTTP ${res.status}`);
53
+ return body;
54
+ }
55
+ // ── MCP Server ────────────────────────────────────────────────────────────────
56
+ const server = new index_js_1.Server({ name: 'vantage-mcp', version: '1.0.0' }, { capabilities: { tools: {}, resources: {} } });
57
+ // ── Tool definitions ──────────────────────────────────────────────────────────
58
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
59
+ tools: [
60
+ {
61
+ name: 'track_llm_call',
62
+ description: 'Track an LLM API call — logs cost, tokens, latency, model, and team to VantageAI. Call this after every LLM completion.',
63
+ inputSchema: {
64
+ type: 'object',
65
+ properties: {
66
+ model: { type: 'string', description: 'Model name, e.g. gpt-4o, claude-3-5-sonnet' },
67
+ provider: { type: 'string', description: 'Provider: openai | anthropic | google | mistral | cohere | other' },
68
+ prompt_tokens: { type: 'number', description: 'Number of input/prompt tokens' },
69
+ completion_tokens: { type: 'number', description: 'Number of output/completion tokens' },
70
+ total_cost_usd: { type: 'number', description: 'Total cost in USD (e.g. 0.0025)' },
71
+ latency_ms: { type: 'number', description: 'End-to-end latency in milliseconds' },
72
+ team: { type: 'string', description: 'Team or feature name for grouping (e.g. "backend", "search")' },
73
+ environment: { type: 'string', description: 'Environment: production | staging | development' },
74
+ trace_id: { type: 'string', description: 'Trace ID for grouping multi-step agent calls' },
75
+ span_depth: { type: 'number', description: 'Depth in agent call tree (0 = root)' },
76
+ tags: { type: 'object', description: 'Arbitrary key-value tags for filtering' },
77
+ },
78
+ required: ['model', 'provider', 'prompt_tokens', 'completion_tokens', 'total_cost_usd'],
79
+ },
80
+ },
81
+ {
82
+ name: 'get_summary',
83
+ description: 'Get a high-level cost summary: total spend this month, number of requests, avg latency, top model, and budget status.',
84
+ inputSchema: { type: 'object', properties: {} },
85
+ },
86
+ {
87
+ name: 'get_kpis',
88
+ description: 'Get detailed KPI metrics: MTD cost, daily cost, P50/P95 latency, efficiency score, error rate, active models and teams.',
89
+ inputSchema: { type: 'object', properties: {} },
90
+ },
91
+ {
92
+ name: 'get_model_breakdown',
93
+ description: 'Get cost and usage breakdown per LLM model — useful for identifying expensive models or optimization opportunities.',
94
+ inputSchema: {
95
+ type: 'object',
96
+ properties: {
97
+ days: { type: 'number', description: 'Look-back window in days (default: 30)' },
98
+ },
99
+ },
100
+ },
101
+ {
102
+ name: 'get_team_breakdown',
103
+ description: 'Get cost and usage breakdown per team — useful for chargeback reporting or finding which feature drives the most spend.',
104
+ inputSchema: {
105
+ type: 'object',
106
+ properties: {
107
+ days: { type: 'number', description: 'Look-back window in days (default: 30)' },
108
+ },
109
+ },
110
+ },
111
+ {
112
+ name: 'check_budget',
113
+ description: 'Check current budget status — returns % of monthly budget used, remaining budget, and whether the org is over limit.',
114
+ inputSchema: { type: 'object', properties: {} },
115
+ },
116
+ {
117
+ name: 'get_traces',
118
+ description: 'Get recent multi-step agent traces — shows the full call tree, per-span cost, and total trace cost.',
119
+ inputSchema: {
120
+ type: 'object',
121
+ properties: {
122
+ limit: { type: 'number', description: 'Number of traces to return (default: 10, max: 50)' },
123
+ },
124
+ },
125
+ },
126
+ {
127
+ name: 'get_cost_gate',
128
+ description: 'CI/CD cost gate — returns whether spend in the current period is within the configured budget. Use in CI pipelines before merging.',
129
+ inputSchema: {
130
+ type: 'object',
131
+ properties: {
132
+ period: { type: 'string', description: 'Period to check: today | week | month (default: today)' },
133
+ },
134
+ },
135
+ },
136
+ ],
137
+ }));
138
+ // ── Tool handlers ─────────────────────────────────────────────────────────────
139
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
140
+ const { name, arguments: args = {} } = request.params;
141
+ try {
142
+ switch (name) {
143
+ case 'track_llm_call': {
144
+ const event = {
145
+ event_id: `mcp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
146
+ ...args,
147
+ org_id: ORG,
148
+ };
149
+ await api('/v1/events', { method: 'POST', body: JSON.stringify(event) });
150
+ return {
151
+ content: [{
152
+ type: 'text',
153
+ text: `✅ Tracked: ${args.model} | ${args.prompt_tokens}→${args.completion_tokens} tokens | $${Number(args.total_cost_usd).toFixed(4)} | ${args.latency_ms ? `${args.latency_ms}ms` : 'no latency recorded'}`,
154
+ }],
155
+ };
156
+ }
157
+ case 'get_summary': {
158
+ const data = await api('/v1/analytics/summary');
159
+ const lines = [
160
+ `📊 **VantageAI Summary** (org: ${ORG})`,
161
+ ``,
162
+ `| Metric | Value |`,
163
+ `|--------|-------|`,
164
+ `| MTD Spend | $${Number(data.mtd_cost ?? 0).toFixed(2)} |`,
165
+ `| Today | $${Number(data.today_cost ?? 0).toFixed(2)} |`,
166
+ `| Requests | ${Number(data.total_requests ?? 0).toLocaleString()} |`,
167
+ `| Avg Latency | ${Number(data.avg_latency_ms ?? 0).toFixed(0)}ms |`,
168
+ `| Top Model | ${data.top_model ?? 'N/A'} |`,
169
+ `| Budget Used | ${data.budget_pct != null ? `${Number(data.budget_pct).toFixed(1)}%` : 'No budget set'} |`,
170
+ ``,
171
+ `🔗 [View dashboard](https://vantageaiops.com/app.html)`,
172
+ ];
173
+ return { content: [{ type: 'text', text: lines.join('\n') }] };
174
+ }
175
+ case 'get_kpis': {
176
+ const data = await api('/v1/analytics/kpis');
177
+ const rows = data.map((r) => `| ${r.label} | ${r.value} | ${r.delta != null ? (Number(r.delta) >= 0 ? `+${r.delta}` : r.delta) : '—'} |`);
178
+ const text = [
179
+ `📈 **KPIs** (org: ${ORG})`,
180
+ ``,
181
+ `| Metric | Value | Δ |`,
182
+ `|--------|-------|---|`,
183
+ ...rows,
184
+ ].join('\n');
185
+ return { content: [{ type: 'text', text }] };
186
+ }
187
+ case 'get_model_breakdown': {
188
+ const days = args?.days ?? 30;
189
+ const data = await api(`/v1/analytics/models?days=${days}`);
190
+ const rows = data.map((r) => `| ${r.model} | $${Number(r.total_cost).toFixed(4)} | ${Number(r.requests).toLocaleString()} | ${Number(r.avg_latency_ms ?? 0).toFixed(0)}ms |`);
191
+ const text = [
192
+ `🤖 **Model Breakdown** (last ${days} days)`,
193
+ ``,
194
+ `| Model | Cost | Requests | Avg Latency |`,
195
+ `|-------|------|----------|-------------|`,
196
+ ...rows,
197
+ ].join('\n');
198
+ return { content: [{ type: 'text', text }] };
199
+ }
200
+ case 'get_team_breakdown': {
201
+ const days = args?.days ?? 30;
202
+ const data = await api(`/v1/analytics/teams?days=${days}`);
203
+ const rows = data.map((r) => `| ${r.team || '(untagged)'} | $${Number(r.total_cost).toFixed(4)} | ${Number(r.requests).toLocaleString()} |`);
204
+ const text = [
205
+ `👥 **Team Breakdown** (last ${days} days)`,
206
+ ``,
207
+ `| Team | Cost | Requests |`,
208
+ `|------|------|----------|`,
209
+ ...rows,
210
+ ].join('\n');
211
+ return { content: [{ type: 'text', text }] };
212
+ }
213
+ case 'check_budget': {
214
+ const data = await api('/v1/analytics/summary');
215
+ const pct = Number(data.budget_pct ?? 0);
216
+ const mtd = Number(data.mtd_cost ?? 0);
217
+ const budget = Number(data.budget_usd ?? 0);
218
+ const status = budget === 0 ? '⚪ No budget set'
219
+ : pct >= 100 ? '🚨 OVER BUDGET'
220
+ : pct >= 80 ? '⚠️ Approaching limit'
221
+ : '✅ Within budget';
222
+ const text = [
223
+ `💰 **Budget Status** (org: ${ORG})`,
224
+ ``,
225
+ `${status}`,
226
+ ``,
227
+ `| | |`,
228
+ `|-|-|`,
229
+ `| MTD Spend | $${mtd.toFixed(2)} |`,
230
+ `| Budget | ${budget ? `$${budget.toFixed(2)}` : 'Not set'} |`,
231
+ `| Used | ${budget ? `${pct.toFixed(1)}%` : '—'} |`,
232
+ `| Remaining | ${budget ? `$${Math.max(0, budget - mtd).toFixed(2)}` : '—'} |`,
233
+ ].join('\n');
234
+ return { content: [{ type: 'text', text }] };
235
+ }
236
+ case 'get_traces': {
237
+ const limit = Math.min(Number(args?.limit ?? 10), 50);
238
+ const data = await api(`/v1/analytics/traces?limit=${limit}`);
239
+ if (!data.length) {
240
+ return { content: [{ type: 'text', text: 'No traces found. Make sure to pass `trace_id` when calling `track_llm_call`.' }] };
241
+ }
242
+ const rows = data.map((t) => `| ${String(t.trace_id).slice(0, 16)}… | ${t.span_count} spans | $${Number(t.total_cost).toFixed(4)} | ${t.root_model ?? 'N/A'} |`);
243
+ const text = [
244
+ `🔍 **Recent Agent Traces** (last ${limit})`,
245
+ ``,
246
+ `| Trace ID | Spans | Total Cost | Root Model |`,
247
+ `|----------|-------|------------|------------|`,
248
+ ...rows,
249
+ ].join('\n');
250
+ return { content: [{ type: 'text', text }] };
251
+ }
252
+ case 'get_cost_gate': {
253
+ const period = args?.period ?? 'today';
254
+ const data = await api(`/v1/analytics/cost?period=${period}`);
255
+ const passed = data.within_budget;
256
+ const text = [
257
+ `🚦 **CI Cost Gate** — ${passed ? '✅ PASSED' : '❌ FAILED'}`,
258
+ ``,
259
+ `| | |`,
260
+ `|-|-|`,
261
+ `| Period | ${period} |`,
262
+ `| Spend | $${Number(data.cost ?? 0).toFixed(4)} |`,
263
+ `| Budget | $${Number(data.budget ?? 0).toFixed(2)} |`,
264
+ `| Status | ${passed ? 'Within budget' : '**Over budget — block merge**'} |`,
265
+ ].join('\n');
266
+ return { content: [{ type: 'text', text }] };
267
+ }
268
+ default:
269
+ throw new Error(`Unknown tool: ${name}`);
270
+ }
271
+ }
272
+ catch (err) {
273
+ return {
274
+ content: [{ type: 'text', text: `❌ Error: ${err.message}` }],
275
+ isError: true,
276
+ };
277
+ }
278
+ });
279
+ // ── Resources ─────────────────────────────────────────────────────────────────
280
+ server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => ({
281
+ resources: [
282
+ {
283
+ uri: 'vantage://dashboard',
284
+ name: 'VantageAI Dashboard',
285
+ description: 'Live cost analytics dashboard',
286
+ mimeType: 'text/plain',
287
+ },
288
+ {
289
+ uri: 'vantage://docs',
290
+ name: 'VantageAI Docs',
291
+ description: 'SDK integration guides and API reference',
292
+ mimeType: 'text/plain',
293
+ },
294
+ {
295
+ uri: 'vantage://config',
296
+ name: 'Current MCP Config',
297
+ description: 'Active API key, org, and base URL',
298
+ mimeType: 'text/plain',
299
+ },
300
+ ],
301
+ }));
302
+ server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) => {
303
+ const { uri } = request.params;
304
+ switch (uri) {
305
+ case 'vantage://dashboard':
306
+ return { contents: [{ uri, mimeType: 'text/plain', text: 'Dashboard: https://vantageaiops.com/app.html' }] };
307
+ case 'vantage://docs':
308
+ return { contents: [{ uri, mimeType: 'text/plain', text: 'Docs: https://vantageaiops.com/docs.html' }] };
309
+ case 'vantage://config':
310
+ return {
311
+ contents: [{
312
+ uri,
313
+ mimeType: 'text/plain',
314
+ text: [
315
+ `API Base : ${API_BASE}`,
316
+ `Org : ${ORG}`,
317
+ `API Key : ${API_KEY ? `${API_KEY.slice(0, 8)}${'*'.repeat(Math.max(0, API_KEY.length - 8))}` : '(not set)'}`,
318
+ ].join('\n'),
319
+ }],
320
+ };
321
+ default:
322
+ throw new Error(`Unknown resource: ${uri}`);
323
+ }
324
+ });
325
+ // ── Start ─────────────────────────────────────────────────────────────────────
326
+ async function main() {
327
+ const transport = new stdio_js_1.StdioServerTransport();
328
+ await server.connect(transport);
329
+ // MCP servers must not write to stdout — use stderr for logs
330
+ process.stderr.write('[vantage-mcp] Server started\n');
331
+ }
332
+ main().catch((err) => {
333
+ process.stderr.write(`[vantage-mcp] Fatal: ${err.message}\n`);
334
+ process.exit(1);
335
+ });
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "vantageaiops-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for VantageAI — track LLM costs and query analytics from Claude Desktop, Cursor, Windsurf, VS Code and more",
5
+ "private": false,
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "vantageaiops-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": ["mcp", "model-context-protocol", "llm", "cost", "analytics", "vantage", "ai", "openai", "anthropic", "claude", "cursor", "windsurf"],
17
+ "homepage": "https://vantageaiops.com",
18
+ "repository": { "type": "git", "url": "https://github.com/Amanjain98/VantageAI" },
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "@modelcontextprotocol/sdk": "^1.0.0"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.4.0",
25
+ "@types/node": "^20.0.0"
26
+ },
27
+ "files": ["dist", "README.md"],
28
+ "engines": { "node": ">=18.0.0" }
29
+ }