Skip to content

Commit f83b9db

Browse files
committed
feat: add NPM download statistics API and update README badges
1 parent 709a76c commit f83b9db

6 files changed

Lines changed: 53 additions & 25 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# ATV — DeFi Yield Vault MCP Server
22

33
[![npm version](https://img.shields.io/npm/v/@aarna-ai/mcp-server-atv)](https://www.npmjs.com/package/@aarna-ai/mcp-server-atv)
4-
[![npm downloads](https://img.shields.io/npm/dt/@aarna-ai/mcp-server-atv?label=downloads&logo=npm)](https://www.npmjs.com/package/@aarna-ai/mcp-server-atv)
4+
[![npm downloads](https://img.shields.io/npm/dm/@aarna-ai/mcp-server-atv?label=downloads%2Fmonth&logo=npm)](https://www.npmjs.com/package/@aarna-ai/mcp-server-atv)
55
[![smithery badge](https://smithery.ai/badge/aarna-ai/atv)](https://smithery.ai/servers/aarna-ai/atv)
66
[![MCP Registry](https://img.shields.io/badge/MCP_Registry-io.github.aarna--ai%2Fatv-blue)](https://registry.modelcontextprotocol.io/servers/io.github.aarna-ai/atv)
77
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

apps/api/src/handlers/admin.handler.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,42 @@ import { pool } from '../db/postgres';
44
import { env } from '../config/env';
55
import { Tier, TIER_RATE_LIMITS } from '../types/vault.types';
66

7+
const NPM_PACKAGE = '@aarna-ai/mcp-server-atv';
8+
const NPM_BASE = 'https://api.npmjs.org/downloads';
9+
10+
export async function getNpmDownloads(_req: Request, res: Response): Promise<void> {
11+
const today = new Date().toISOString().slice(0, 10);
12+
const pkg = encodeURIComponent(NPM_PACKAGE);
13+
14+
const [rangeRes, monthlyRes, weeklyRes] = await Promise.all([
15+
fetch(`${NPM_BASE}/range/2000-01-01:${today}/${pkg}`),
16+
fetch(`${NPM_BASE}/point/last-month/${pkg}`),
17+
fetch(`${NPM_BASE}/point/last-week/${pkg}`),
18+
]);
19+
20+
if (!rangeRes.ok || !monthlyRes.ok || !weeklyRes.ok) {
21+
res.status(502).json({ error: 'npm registry request failed' });
22+
return;
23+
}
24+
25+
const [rangeData, monthlyData, weeklyData] = await Promise.all([
26+
rangeRes.json() as Promise<{ downloads: { downloads: number; day: string }[] }>,
27+
monthlyRes.json() as Promise<{ downloads: number }>,
28+
weeklyRes.json() as Promise<{ downloads: number }>,
29+
]);
30+
31+
const allDownloads = rangeData.downloads ?? [];
32+
const total = allDownloads.reduce((sum: number, d: { downloads: number }) => sum + d.downloads, 0);
33+
const trend = allDownloads.slice(-30);
34+
35+
res.json({
36+
total,
37+
monthly: monthlyData.downloads ?? 0,
38+
weekly: weeklyData.downloads ?? 0,
39+
trend,
40+
});
41+
}
42+
743
const VALID_PERIODS: Record<string, string> = {
844
'24h': '24 hours',
945
'7d': '7 days',

apps/api/src/routes/admin.routes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ adminRouter.get('/analytics/status-breakdown', admin.getStatusBreakdown);
3030
adminRouter.get('/analytics/user-agents', admin.getUserAgentBreakdown);
3131
adminRouter.get('/analytics/recent', admin.getRecentRequests);
3232

33+
// NPM download stats (proxied to avoid CORS)
34+
adminRouter.get('/analytics/npm-downloads', admin.getNpmDownloads);
35+
3336
// DeFi-specific analytics
3437
adminRouter.get('/analytics/top-vaults', admin.getTopVaults);
3538
adminRouter.get('/analytics/tx-funnel', admin.getTxFunnel);

apps/dashboard/src/api/analytics.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ export const fetchUserAgents = (period: Period) =>
5757
export const fetchRecentRequests = (limit?: number) =>
5858
api.get<RecentRequest[]>('/admin/analytics/recent', { params: { limit } }).then((r) => r.data);
5959

60+
// NPM download stats
61+
export const fetchNpmDownloads = () =>
62+
api.get<{ total: number; monthly: number; weekly: number; trend: { downloads: number; day: string }[] }>(
63+
'/admin/analytics/npm-downloads',
64+
).then((r) => r.data);
65+
6066
// DeFi-specific analytics
6167
export const fetchTopVaults = (period: Period) =>
6268
api.get<TopVault[]>('/admin/analytics/top-vaults', { params: { period } }).then((r) => r.data);

apps/dashboard/src/components/cards/NpmDownloadsCard.tsx

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ import {
88
Tooltip,
99
ResponsiveContainer,
1010
} from 'recharts';
11+
import { fetchNpmDownloads } from '../../api/analytics';
1112

1213
const PACKAGE = '@aarna-ai/mcp-server-atv';
13-
const TODAY = new Date().toISOString().slice(0, 10);
14-
const NPM_BASE = 'https://api.npmjs.org/downloads';
1514

1615
interface TrendPoint {
1716
day: string;
@@ -42,35 +41,19 @@ export function NpmDownloadsCard() {
4241

4342
async function load() {
4443
try {
45-
const [rangeRes, monthlyRes, weeklyRes] = await Promise.all([
46-
fetch(`${NPM_BASE}/range/2000-01-01:${TODAY}/${PACKAGE}`),
47-
fetch(`${NPM_BASE}/point/last-month/${PACKAGE}`),
48-
fetch(`${NPM_BASE}/point/last-week/${PACKAGE}`),
49-
]);
50-
51-
if (!rangeRes.ok || !monthlyRes.ok || !weeklyRes.ok) {
52-
throw new Error('npm registry request failed');
53-
}
54-
55-
const [rangeData, monthlyData, weeklyData] = await Promise.all([
56-
rangeRes.json(),
57-
monthlyRes.json(),
58-
weeklyRes.json(),
59-
]);
44+
const data = await fetchNpmDownloads();
6045

6146
if (cancelled) return;
6247

63-
const allDownloads: TrendPoint[] = rangeData.downloads ?? [];
64-
const total = allDownloads.reduce((sum, d) => sum + d.downloads, 0);
65-
const trend = allDownloads.slice(-30).map((d) => ({
48+
const trend = (data.trend ?? []).map((d) => ({
6649
day: new Date(d.day).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }),
6750
downloads: d.downloads,
6851
}));
6952

7053
setState({
71-
total,
72-
monthly: monthlyData.downloads ?? 0,
73-
weekly: weeklyData.downloads ?? 0,
54+
total: data.total,
55+
monthly: data.monthly,
56+
weekly: data.weekly,
7457
trend,
7558
loading: false,
7659
error: null,

packages/mcp-server/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# @aarna-ai/mcp-server-atv
22

33
[![npm version](https://img.shields.io/npm/v/@aarna-ai/mcp-server-atv)](https://www.npmjs.com/package/@aarna-ai/mcp-server-atv)
4-
[![npm downloads](https://img.shields.io/npm/dt/@aarna-ai/mcp-server-atv?label=downloads&logo=npm)](https://www.npmjs.com/package/@aarna-ai/mcp-server-atv)
4+
[![npm downloads](https://img.shields.io/npm/dm/@aarna-ai/mcp-server-atv?label=downloads%2Fmonth&logo=npm)](https://www.npmjs.com/package/@aarna-ai/mcp-server-atv)
55

66
MCP server connector for **ATV** — AI-native access to Aarna's tokenized DeFi yield vaults on Ethereum and Base.
77

0 commit comments

Comments
 (0)