Sign In

@spanlens/mcp-server

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@spanlens/mcp-server - npm Package Compare versions

Comparing version
0.2.0
to
0.2.1
+16
-0
dist/tools.d.ts

@@ -16,2 +16,18 @@ /**

import type { SpanlensClient } from './client.js';
/**
* Translate the MCP-friendly timeframe enum into hours.
*
* The REST API has no `window` param: /stats/overview takes `from`/`to`
* ISO dates and /stats/models takes `hours`. (v0.2.0 sent `window`, which
* the server silently ignored — stats came back for the wrong period.)
*/
export declare function timeframeToHours(tf?: string): number;
/** ISO timestamp `hours` ago — the `from` bound for /stats/overview. */
export declare function hoursAgoIso(hours: number): string;
/**
* Map an ISO `since` bound onto /api/v1/anomalies' `observationHours`
* param (the server compares the last N hours against a reference window;
* it has no `from` param). Clamped to the server's accepted 0.25–72 range.
*/
export declare function sinceToObservationHours(since: string): number | undefined;
export declare function registerTools(server: McpServer, client: SpanlensClient): void;
+56
-19

@@ -9,16 +9,38 @@ import { z } from 'zod';

};
/** Translate the MCP-friendly enum into the REST API's window param shape. */
function timeframeToWindow(tf) {
/**
* Translate the MCP-friendly timeframe enum into hours.
*
* The REST API has no `window` param: /stats/overview takes `from`/`to`
* ISO dates and /stats/models takes `hours`. (v0.2.0 sent `window`, which
* the server silently ignored — stats came back for the wrong period.)
*/
export function timeframeToHours(tf) {
switch (tf) {
case '1h':
return '1h';
return 1;
case '24h':
return '24h';
return 24;
case '30d':
return '30d';
return 30 * 24;
case '7d':
default:
return '7d';
return 7 * 24;
}
}
/** ISO timestamp `hours` ago — the `from` bound for /stats/overview. */
export function hoursAgoIso(hours) {
return new Date(Date.now() - hours * 3_600_000).toISOString();
}
/**
* Map an ISO `since` bound onto /api/v1/anomalies' `observationHours`
* param (the server compares the last N hours against a reference window;
* it has no `from` param). Clamped to the server's accepted 0.25–72 range.
*/
export function sinceToObservationHours(since) {
const t = Date.parse(since);
if (Number.isNaN(t))
return undefined;
const hours = (Date.now() - t) / 3_600_000;
return Math.min(72, Math.max(0.25, hours));
}
export function registerTools(server, client) {

@@ -37,10 +59,9 @@ // ── 1. get_stats ────────────────────────────────────────────────────────

try {
const hours = timeframeToHours(timeframe);
if (groupBy === 'model' || groupBy === 'provider') {
const data = await client.get('/api/v1/stats/models', {
window: timeframeToWindow(timeframe),
});
const data = await client.get('/api/v1/stats/models', { hours });
return formatJson(data);
}
const data = await client.get('/api/v1/stats/overview', {
window: timeframeToWindow(timeframe),
from: hoursAgoIso(hours),
});

@@ -64,3 +85,14 @@ return formatJson(data);

provider: z
.enum(['openai', 'anthropic', 'gemini', 'azure'])
.enum([
'openai',
'anthropic',
'gemini',
'azure',
'mistral',
'openrouter',
'groq',
'deepseek',
'xai',
'cohere',
])
.optional()

@@ -148,14 +180,19 @@ .describe('Filter to a specific provider.'),

// ── 5. get_anomalies ────────────────────────────────────────────────────
server.tool('get_anomalies', 'List unacknowledged cost / latency / error-rate anomalies the platform has detected. Use when the user asks "anything weird going on?", "any spikes?", or wants a quick health check.', {
severity: z
.enum(['low', 'medium', 'high'])
.optional()
.describe('Filter to anomalies at or above this severity.'),
server.tool('get_anomalies', 'List unacknowledged cost / latency / error-rate anomalies the platform has detected. Each anomaly carries a `deviations` field (how many sigmas off baseline). Use when the user asks "anything weird going on?", "any spikes?", or wants a quick health check.', {
since: z
.string()
.optional()
.describe('ISO 8601 timestamp lower bound. Only return anomalies first seen at or after this time.'),
}, async ({ severity, since }) => {
.describe('ISO 8601 timestamp. Sets the observation window: behaviour since this time is compared against the preceding baseline. Clamped to the last 15 minutes – 72 hours; default is the last hour.'),
sigma: z
.number()
.min(1)
.max(10)
.optional()
.describe('Minimum deviations (in sigmas) to flag. Default 3. Lower = more sensitive.'),
}, async ({ since, sigma }) => {
try {
const data = await client.get('/api/v1/anomalies', { severity, from: since });
const data = await client.get('/api/v1/anomalies', {
observationHours: since ? sinceToObservationHours(since) : undefined,
sigma,
});
return formatJson(data);

@@ -162,0 +199,0 @@ }

@@ -1,1 +0,1 @@

export declare const SERVER_VERSION = "0.2.0";
export declare const SERVER_VERSION = "0.2.1";
// Kept in a separate module so package.json doesn't have to be read at runtime
// (avoids resolveJsonModule pulling the whole manifest into the bundle).
// Bumped manually with each release; package.json stays canonical for npm.
export const SERVER_VERSION = '0.2.0';
export const SERVER_VERSION = '0.2.1';
{
"name": "@spanlens/mcp-server",
"version": "0.2.0",
"version": "0.2.1",
"mcpName": "io.github.spanlens/mcp-server",

@@ -5,0 +5,0 @@ "description": "MCP server for Spanlens — query LLM cost, anomalies, traces, and per-user analytics from inside Cursor, Continue, or Claude Desktop.",

@@ -90,3 +90,3 @@ # @spanlens/mcp-server

| `get_trace` | Full agent span tree for a trace ID — every LLM/tool/retrieval span with timing, tokens, cost. |
| `get_anomalies` | Cost / latency / error-rate anomalies the platform has detected. Optional `severity`. |
| `get_anomalies` | Cost / latency / error-rate anomalies the platform has detected. Optional `since` (observation window) and `sigma` (sensitivity). |
| `get_savings` | Model-swap recommendations with projected monthly savings and adoption status. |

@@ -93,0 +93,0 @@ | `get_user_analytics` | Per-end-user usage breakdown — total cost, request count, models touched, recent calls. |