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.
- package/README.md +183 -0
- package/dist/index.js +335 -0
- 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
|
+
[](https://www.npmjs.com/package/vantageaiops-mcp)
|
|
6
|
+
[](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
|
+
}
|