Skip to content

Commit ecec7d9

Browse files
committed
feat: npm stats apis
1 parent 1e3fd54 commit ecec7d9

File tree

7 files changed

+689
-1
lines changed

7 files changed

+689
-1
lines changed

docs/mcp/overview.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ The MCP server exposes tools for documentation and showcase management:
5353
| `delete_showcase` | Delete your showcase submission | Yes |
5454
| `list_my_showcases` | List your own showcase submissions | Yes |
5555

56+
### NPM Stats Tools
57+
58+
| Tool | Description | Auth Required |
59+
| --------------------------- | ------------------------------------------------------- | ------------- |
60+
| `get_npm_stats` | Get aggregated download stats for TanStack or a library | No |
61+
| `list_npm_comparisons` | List preset package comparisons (Data Fetching, etc.) | No |
62+
| `compare_npm_packages` | Compare download stats for multiple packages over time | No |
63+
| `get_npm_package_downloads` | Get detailed historical downloads for a single package | No |
64+
5665
See [Available Tools](./tools) for detailed parameter documentation.
5766

5867
## How It Works

docs/mcp/tools.md

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,154 @@ List your own showcase submissions. **Requires authentication.** Returns all you
338338

339339
## Rate Limits
340340

341-
- **Read operations** (documentation, search): 60 requests per minute
341+
- **Read operations** (documentation, search, stats): 60 requests per minute
342342
- **Write operations** (submit, update, delete): 10 requests per hour
343343
- **Pending submission limit**: Maximum 5 pending submissions per user
344+
345+
---
346+
347+
## NPM Stats Tools
348+
349+
These tools provide access to NPM download statistics for package analysis and comparison.
350+
351+
### get_npm_stats
352+
353+
Get aggregated NPM download statistics for the TanStack org or a specific library.
354+
355+
#### Parameters
356+
357+
| Parameter | Type | Required | Description |
358+
| --------- | ------ | -------- | ----------------------------------------------------------------------------- |
359+
| `library` | string | No | Filter to specific library (e.g., `query`, `router`). Omit for org-wide stats |
360+
361+
#### Response
362+
363+
For org-wide stats:
364+
365+
- `org` - Organization name ("tanstack")
366+
- `totalDownloads` - Total downloads across all packages
367+
- `ratePerDay` - Current download rate per day
368+
- `updatedAt` - When stats were last updated
369+
- `libraries` - Breakdown by library with `id`, `totalDownloads`, `packageCount`
370+
371+
For library-specific stats:
372+
373+
- `library` - Library ID
374+
- `totalDownloads` - Total downloads for this library
375+
- `ratePerDay` - Current download rate per day
376+
- `packageCount` - Number of packages in this library
377+
- `packages` - Array of packages with `name`, `downloads`, `ratePerDay`
378+
379+
#### Example
380+
381+
```json
382+
{
383+
"name": "get_npm_stats",
384+
"arguments": {
385+
"library": "query"
386+
}
387+
}
388+
```
389+
390+
---
391+
392+
### list_npm_comparisons
393+
394+
List available preset package comparisons for common categories like Data Fetching, State Management, Routing, etc.
395+
396+
#### Parameters
397+
398+
| Parameter | Type | Required | Description |
399+
| ---------- | ------ | -------- | -------------------------------------------------------- |
400+
| `category` | string | No | Filter by category name (case-insensitive partial match) |
401+
402+
#### Response
403+
404+
- `comparisons` - Array of comparisons with `id`, `name`, `packages`
405+
- `total` - Number of comparisons returned
406+
407+
#### Available Categories
408+
409+
Data Fetching, State Management, Routing (React), Data Grids, Virtualization, Frameworks, Styling, Build Tools, Testing, Forms, UI Components, Animation, Date & Time, Validation, Documentation, All TanStack Packages
410+
411+
#### Example
412+
413+
```json
414+
{
415+
"name": "list_npm_comparisons",
416+
"arguments": {
417+
"category": "state"
418+
}
419+
}
420+
```
421+
422+
---
423+
424+
### compare_npm_packages
425+
426+
Compare NPM download statistics for multiple packages over a time range. Returns binned download data for analysis.
427+
428+
#### Parameters
429+
430+
| Parameter | Type | Required | Description |
431+
| ---------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------- |
432+
| `packages` | string[] | Yes | Package names to compare (max 10) |
433+
| `range` | string | No | Time range: `7-days`, `30-days`, `90-days`, `180-days`, `365-days`, `730-days`, `all-time`. Default: `30-days` |
434+
| `binType` | string | No | Aggregation: `daily`, `weekly`, `monthly`. Default: `weekly` |
435+
436+
#### Response
437+
438+
- `packages` - Array of package data with:
439+
- `name` - Package name
440+
- `totalDownloads` - Total downloads in range
441+
- `averagePerDay` - Average downloads per day
442+
- `data` - Array of `{ date, downloads }` binned by `binType`
443+
- `range` - Object with `start` and `end` dates
444+
- `binType` - The aggregation level used
445+
446+
#### Example
447+
448+
```json
449+
{
450+
"name": "compare_npm_packages",
451+
"arguments": {
452+
"packages": ["@tanstack/react-query", "swr", "@apollo/client"],
453+
"range": "90-days",
454+
"binType": "weekly"
455+
}
456+
}
457+
```
458+
459+
---
460+
461+
### get_npm_package_downloads
462+
463+
Get detailed historical download data for a single NPM package.
464+
465+
#### Parameters
466+
467+
| Parameter | Type | Required | Description |
468+
| --------- | ------ | -------- | ------------------------------------------------------- |
469+
| `package` | string | Yes | Package name (e.g., `@tanstack/react-query`) |
470+
| `year` | string | No | Year in YYYY format or `current`. Default: current year |
471+
472+
#### Response
473+
474+
- `package` - Package name
475+
- `year` - The year requested
476+
- `totalDownloads` - Total downloads for the year
477+
- `dayCount` - Number of days with data
478+
- `averagePerDay` - Average downloads per day
479+
- `data` - Array of daily downloads with `{ day, downloads }`
480+
481+
#### Example
482+
483+
```json
484+
{
485+
"name": "get_npm_package_downloads",
486+
"arguments": {
487+
"package": "@tanstack/react-query",
488+
"year": "2024"
489+
}
490+
}
491+
```

src/mcp/server.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@ import {
1414
listMyShowcases,
1515
listMyShowcasesSchema,
1616
} from './tools/list-my-showcases'
17+
import { getNpmStats, getNpmStatsSchema } from './tools/get-npm-stats'
18+
import {
19+
listNpmComparisons,
20+
listNpmComparisonsSchema,
21+
} from './tools/list-npm-comparisons'
22+
import {
23+
compareNpmPackages,
24+
compareNpmPackagesSchema,
25+
} from './tools/compare-npm-packages'
26+
import {
27+
getNpmPackageDownloads,
28+
getNpmPackageDownloadsSchema,
29+
} from './tools/get-npm-package-downloads'
1730

1831
export type McpAuthContext = {
1932
userId: string
@@ -298,5 +311,133 @@ export function createMcpServer(authContext?: McpAuthContext) {
298311
},
299312
)
300313

314+
// ============================================================================
315+
// NPM Stats Tools
316+
// ============================================================================
317+
318+
// Register get_npm_stats tool
319+
server.tool(
320+
'get_npm_stats',
321+
'Get aggregated NPM download statistics for TanStack org or a specific library. Returns total downloads, growth rate, and package breakdown.',
322+
getNpmStatsSchema.shape,
323+
async (args) => {
324+
try {
325+
const parsed = getNpmStatsSchema.parse(args)
326+
const result = await getNpmStats(parsed)
327+
return {
328+
content: [
329+
{
330+
type: 'text' as const,
331+
text: JSON.stringify(result, null, 2),
332+
},
333+
],
334+
}
335+
} catch (error) {
336+
return {
337+
content: [
338+
{
339+
type: 'text' as const,
340+
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
341+
},
342+
],
343+
isError: true,
344+
}
345+
}
346+
},
347+
)
348+
349+
// Register list_npm_comparisons tool
350+
server.tool(
351+
'list_npm_comparisons',
352+
'List available preset package comparisons (Data Fetching, State Management, Routing, etc.). Use with compare_npm_packages for quick comparisons.',
353+
listNpmComparisonsSchema.shape,
354+
async (args) => {
355+
try {
356+
const parsed = listNpmComparisonsSchema.parse(args)
357+
const result = await listNpmComparisons(parsed)
358+
return {
359+
content: [
360+
{
361+
type: 'text' as const,
362+
text: JSON.stringify(result, null, 2),
363+
},
364+
],
365+
}
366+
} catch (error) {
367+
return {
368+
content: [
369+
{
370+
type: 'text' as const,
371+
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
372+
},
373+
],
374+
isError: true,
375+
}
376+
}
377+
},
378+
)
379+
380+
// Register compare_npm_packages tool
381+
server.tool(
382+
'compare_npm_packages',
383+
'Compare NPM download statistics for multiple packages over a time range. Returns daily/weekly/monthly download data for visualization and analysis.',
384+
compareNpmPackagesSchema.shape,
385+
async (args) => {
386+
try {
387+
const parsed = compareNpmPackagesSchema.parse(args)
388+
const result = await compareNpmPackages(parsed)
389+
return {
390+
content: [
391+
{
392+
type: 'text' as const,
393+
text: JSON.stringify(result, null, 2),
394+
},
395+
],
396+
}
397+
} catch (error) {
398+
return {
399+
content: [
400+
{
401+
type: 'text' as const,
402+
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
403+
},
404+
],
405+
isError: true,
406+
}
407+
}
408+
},
409+
)
410+
411+
// Register get_npm_package_downloads tool
412+
server.tool(
413+
'get_npm_package_downloads',
414+
'Get detailed historical download data for a single NPM package. Returns daily download counts for analysis.',
415+
getNpmPackageDownloadsSchema.shape,
416+
async (args) => {
417+
try {
418+
const parsed = getNpmPackageDownloadsSchema.parse(args)
419+
const result = await getNpmPackageDownloads(parsed)
420+
return {
421+
content: [
422+
{
423+
type: 'text' as const,
424+
text: JSON.stringify(result, null, 2),
425+
},
426+
],
427+
}
428+
} catch (error) {
429+
return {
430+
content: [
431+
{
432+
type: 'text' as const,
433+
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
434+
},
435+
],
436+
isError: true,
437+
}
438+
}
439+
},
440+
)
441+
301442
return server
302443
}

0 commit comments

Comments
 (0)