projectpulse-mcp 1.3.0 → 1.4.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 CHANGED
@@ -1,117 +1,152 @@
1
- # projectpulse-mcp
1
+ <div id="top" align="center">
2
2
 
3
- GitHub repository health monitoring via MCP.
3
+ <img src="https://raw.githubusercontent.com/alexbypa/github-projectpulse-mcp/main/docs/images/code-scanning-example.png" width="80%" alt="ProjectPulse MCP Logo" style="border-radius: 12px; box-shadow: 0 8px 24px rgba(106, 13, 173, 0.2); margin-bottom: 20px;"/>
4
4
 
5
- This Model Context Protocol (MCP) server allows AI assistants to analyze the health, security, and maintenance status of GitHub repositories directly from your conversations.
5
+ # ⚡ ProjectPulse MCP
6
6
 
7
- ## Features
7
+ **Monitor, score, and compare GitHub repository health directly from your AI agent conversations.**
8
8
 
9
- - **Repository Health Score**: Calculates a comprehensive 0-100 score based on CI status, freshness, security, and community metrics.
10
- - **Basic Repository Info**: Fetches general metadata like stars, forks, language, and license.
11
- - **Dependency Analysis**: Lists Dependabot alerts with severity filtering.
12
- - **CI/CD Status**: Checks recent GitHub Actions workflow runs and their conclusions.
13
- - **Code Scanning**: Retrieves CodeQL and other code scanning alerts.
14
- - **Ping**: Simple connectivity test tool.
9
+ [![npm version](https://img.shields.io/npm/v/projectpulse-mcp.svg?style=for-the-badge&color=6A0DAD&logo=npm&logoColor=white)](https://www.npmjs.com/package/projectpulse-mcp)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-6A0DAD.svg?style=for-the-badge&logo=opensourceinitiative&logoColor=white)](https://opensource.org/licenses/MIT)
11
+ [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC.svg?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
12
+ [![Model Context Protocol](https://img.shields.io/badge/MCP-Protocol-6A0DAD.svg?style=for-the-badge&logo=anthropic&logoColor=white)](https://modelcontextprotocol.io)
13
+ [![Vitest](https://img.shields.io/badge/Tests-58%20Passed-2EA44F.svg?style=for-the-badge&logo=vitest&logoColor=white)](https://vitest.dev/)
15
14
 
16
- ## What's New (v1.3.0)
15
+ <p align="center">
16
+ <a href="#-overview">Overview</a> •
17
+ <a href="#-features">Features</a> •
18
+ <a href="#-tools-reference">Tools</a> •
19
+ <a href="#-quick-start">Quick Start</a> •
20
+ <a href="#-configuration">Configuration</a> •
21
+ <a href="#-license">License</a>
22
+ </p>
17
23
 
18
- **DORA Metrics** — New `get_dora_metrics` tool that calculates proxy [DORA metrics](https://dora.dev/) for any GitHub repository:
24
+ ---
19
25
 
20
- - **Deployment Frequency** — how often releases are published (releases per week)
21
- - **Lead Time for Changes** — median time from PR creation to merge (in hours)
22
- - **Change Failure Rate** — percentage of CI workflow runs that fail
23
- - **Mean Time to Recovery (MTTR)** — median time between a CI failure and the next success on the same branch
26
+ </div>
24
27
 
25
- All metrics are calculated from public GitHub data (releases, pull requests, workflow runs) with a configurable analysis window (7-90 days, default 30). Metrics return `null` when insufficient data is available, so the tool works safely on any repository.
28
+ ## 🌟 Overview
26
29
 
27
- ### Example Output
30
+ **ProjectPulse MCP** is a comprehensive [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server engineered for AI assistants (Claude Desktop, Cursor, Windsurf, Claude Code CLI). It equips LLMs with real-time capabilities to evaluate codebase maintainability, monitor CI/CD reliability, audit security vulnerabilities, track DORA engineering metrics, and rank multiple repositories side-by-side.
28
31
 
29
- ![Code Scanning Example](https://raw.githubusercontent.com/alexbypa/github-projectpulse-mcp/main/docs/images/code-scanning-example.png)
32
+ ---
30
33
 
31
- ## Quick Start
34
+ ## 🚀 Features
32
35
 
33
- ### Claude Code (CLI)
36
+ ### 🔍 1. Multi-Repository Benchmark & Comparison
37
+ * **`compare_repos`**: Compares 2 to 5 repositories simultaneously and returns a ranked JSON score.
38
+ * **Competition Ranking**: Employs standard competition ranking (1224 scheme) with full tie-breaking.
39
+ * **Fault Tolerance**: Isolates repository errors (e.g. 404s) so remaining repositories are scored seamlessly.
34
40
 
35
- To use this MCP server with Claude Code, run the following command in your terminal:
41
+ ### 📊 2. Algorithmic Health Scoring (Grade A–F)
42
+ * **`get_health_score`**: Weighted composite metric across 5 dimensions:
43
+ - **CI/CD Reliability (25%)**: Evaluates pass rate across recent workflow runs.
44
+ - **Security & Dependabot (25%)**: Audits open critical, high, medium, and low security advisories.
45
+ - **Codebase Freshness (20%)**: Tracks commit recency and push activity.
46
+ - **Community & Activity (15%)**: Measures open issue resolution and fork engagement.
47
+ - **Maintenance Standards (15%)**: Assesses licensing, descriptions, and active status.
36
48
 
37
- ```bash
38
- claude mcp add projectpulse -- npx projectpulse-mcp
39
- ```
49
+ ### ⏱️ 3. Proxy DORA Metrics
50
+ * **`get_dora_metrics`**: Computes core DevOps Research & Assessment metrics over a configurable window (7–90 days):
51
+ - **Deployment Frequency**: Weekly release throughput.
52
+ - **Lead Time for Changes**: Median PR duration from opening to merge.
53
+ - **Change Failure Rate**: Proportion of failed workflow executions.
54
+ - **Mean Time to Recovery (MTTR)**: Median duration between failure and subsequent branch recovery.
55
+
56
+ ### 🛡️ 4. Security & Code Quality Audits
57
+ * **`analyze_dependencies`**: Detailed Dependabot alert breakdowns with severity filtering.
58
+ * **`analyze_code_scanning`**: CodeQL static analysis and security scanning summaries.
59
+ * **`check_ci_status`**: Live inspection of recent GitHub Actions pipeline conclusions.
60
+ * **`get_repo_health`**: Raw telemetry (stars, forks, languages, licenses, default branches).
61
+
62
+ ---
40
63
 
41
- *Note: You must have a `.env` file with your `GITHUB_TOKEN` in the directory where you run Claude Code.*
64
+ ## 🛠️ Tools Reference
42
65
 
43
- ### Claude Desktop Configuration
66
+ | Tool Name | Description | Input Schema |
67
+ | :--- | :--- | :--- |
68
+ | **`compare_repos`** | Compares and ranks 2 to 5 GitHub repositories by health score using standard competition ranking. | `repos: Array<{ owner: string, repo: string }>` *(min: 2, max: 5)* |
69
+ | **`get_health_score`** | Calculates a 0–100 weighted health score with letter grade (A–F), category breakdown, and suggestions. | `owner: string`, `repo: string` |
70
+ | **`get_dora_metrics`** | Calculates DORA proxy metrics (Deployment Frequency, Lead Time, Change Failure Rate, MTTR). | `owner: string`, `repo: string`, `days?: number` *(7–90, default: 30)* |
71
+ | **`get_repo_health`** | Retrieves core repository metadata (stars, open issues, language, license, activity). | `owner: string`, `repo: string` |
72
+ | **`analyze_dependencies`** | Lists Dependabot security advisories grouped by severity level. | `owner: string`, `repo: string`, `severity?: "critical" \| "high" \| "medium" \| "low"` |
73
+ | **`analyze_code_scanning`** | Fetches CodeQL and static analysis scanning alerts. | `owner: string`, `repo: string` |
74
+ | **`check_ci_status`** | Fetches recent GitHub Actions workflow runs and conclusions. | `owner: string`, `repo: string`, `limit?: number` *(default: 10)* |
75
+ | **`ping`** | Simple connectivity and latency validation check. | `message: string` |
76
+
77
+ ---
78
+
79
+ ## 📦 Quick Start
44
80
 
45
- To use this with Claude Desktop, add the following configuration to your `claude_desktop_config.json` file (usually located at `%APPDATA%\Claude\claude_desktop_config.json` on Windows or `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
81
+ ### Option A: Claude Desktop Setup
82
+
83
+ Add the server to your `claude_desktop_config.json`:
84
+ * **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
85
+ * **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
46
86
 
47
87
  ```json
48
88
  {
49
89
  "mcpServers": {
50
90
  "projectpulse": {
51
91
  "command": "npx",
52
- "args": [
53
- "-y",
54
- "projectpulse-mcp"
55
- ],
92
+ "args": ["-y", "projectpulse-mcp"],
56
93
  "env": {
57
- "GITHUB_TOKEN": "your-github-personal-access-token"
94
+ "GITHUB_TOKEN": "ghp_your_github_personal_access_token"
58
95
  }
59
96
  }
60
97
  }
61
98
  }
62
99
  ```
63
100
 
64
- ### Other MCP Clients (Cursor, Windsurf, etc.)
101
+ ### Option B: Claude Code CLI
102
+
103
+ ```bash
104
+ claude mcp add projectpulse -- npx projectpulse-mcp
105
+ ```
106
+
107
+ ### Option C: Cursor & Other MCP Clients (stdio transport)
108
+ * **Command:** `npx`
109
+ * **Args:** `-y projectpulse-mcp`
110
+ * **Environment Variable:** `GITHUB_TOKEN=your_token_here`
65
111
 
66
- To use this MCP server in other clients that support `stdio` transport (like Cursor, Windsurf, or custom applications), configure a new MCP server with the following settings:
112
+ ---
67
113
 
68
- - **Type/Transport**: `stdio`
69
- - **Command**: `npx`
70
- - **Arguments**: `-y projectpulse-mcp`
71
- - **Environment Variables**: Add `GITHUB_TOKEN` with your GitHub Personal Access Token.
114
+ ## 🔑 Configuration & GitHub Tokens
72
115
 
73
- ## Tools
116
+ To prevent GitHub API rate-limiting and allow reading security advisories (Dependabot/CodeQL), generate a GitHub Personal Access Token (PAT):
74
117
 
75
- | Tool Name | Description | Input Schema |
76
- | --- | --- | --- |
77
- | `get_health_score` | Calculate a 0-100 health score for a GitHub repository with grade (A-F), category breakdown, and improvement suggestions. | `owner` (string), `repo` (string) |
78
- | `get_repo_health` | Get health information for a GitHub repository (stars, issues, language, license, etc.). | `owner` (string), `repo` (string) |
79
- | `analyze_dependencies` | Get Dependabot alerts for a GitHub repository. | `owner` (string), `repo` (string), `severity` (optional enum: critical, high, medium, low) |
80
- | `check_ci_status` | Get recent CI/CD workflow runs for a GitHub repository. | `owner` (string), `repo` (string), `limit` (optional number, default: 10) |
81
- | `analyze_code_scanning` | Get Code Scanning alerts for a GitHub repository with CodeQL. | `owner` (string), `repo` (string) |
82
- | `get_dora_metrics` | Calculate DORA proxy metrics (deployment frequency, lead time, change failure rate, MTTR) for a GitHub repository. | `owner` (string), `repo` (string), `days` (optional number, default: 30, range: 7-90) |
83
- | `ping` | Simple connectivity check that responds with a pong. | `message` (string) |
84
-
85
- ## Configuration
86
-
87
- ### GITHUB_TOKEN setup
88
-
89
- To use the GitHub API without severe rate limits and to access protected data (like security alerts), you must provide a GitHub Personal Access Token (PAT).
90
-
91
- **Option A: Fine-grained Personal Access Token (Recommended)**
92
- 1. Go to **Settings** > **Developer settings** > **Personal access tokens** > **Fine-grained tokens**.
93
- 2. Click **Generate new token**.
94
- 3. Under **Repository access**, select the repositories you want to analyze.
95
- 4. Under **Repository permissions**, grant **Read-only** access to:
118
+ 1. Navigate to **GitHub** > **Settings** > **Developer settings** > **Personal access tokens** > **Fine-grained tokens**.
119
+ 2. Grant **Read-only** permissions to:
96
120
  - `Code scanning alerts`
97
121
  - `Dependabot alerts`
98
- - `Metadata` (mandatory default)
122
+ - `Actions`
123
+ - `Metadata` (default)
124
+ 3. Set your token as the `GITHUB_TOKEN` environment variable in your `.env` file or MCP client configuration.
99
125
 
100
- **Option B: Classic Token**
101
- Generate a classic token with at least the `repo` and `security_events` scopes.
126
+ ---
102
127
 
103
- #### Providing the token to the MCP server
128
+ ## 🧪 Development & Testing
104
129
 
105
- **For Claude Desktop:**
106
- Set the `GITHUB_TOKEN` environment variable in your `claude_desktop_config.json` as shown in the Quick Start section.
130
+ ```bash
131
+ # Install dependencies
132
+ npm install
133
+
134
+ # Run comprehensive test suite (58 unit tests)
135
+ npm test
136
+
137
+ # Type-check TypeScript codebase
138
+ npm run lint
107
139
 
108
- **For Claude Code (CLI) or Local Testing:**
109
- Create a `.env` file in the root of this project (or your working directory) with the following content:
110
- ```env
111
- GITHUB_TOKEN=ghp_your_token_here
140
+ # Build production bundle
141
+ npm run build
112
142
  ```
113
- The MCP server automatically loads environment variables from `.env` files on startup.
114
143
 
115
- ## License
144
+ ---
145
+
146
+ ## 📄 License
147
+
148
+ Distributed under the **MIT License**. See [`LICENSE`](./LICENSE) for more information.
116
149
 
117
- MIT License. See the `LICENSE` file for details.
150
+ <div align="center">
151
+ <sub>Built with ❤️ by <a href="https://github.com/alexbypa">Alessandro Chiodo</a></sub>
152
+ </div>
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import { registerAnalyzeDependencies } from "./tools/analyze-dependencies.js";
7
7
  import { registerAnalyzeCodeScanning } from "./tools/analyze-code-scanning.js";
8
8
  import { registerGetHealthScore } from "./tools/get-health-score.js";
9
9
  import { registerGetDoraMetrics } from "./tools/get-dora-metrics.js";
10
+ import { registerCompareRepos } from "./tools/compare-repos.js";
10
11
  const server = new McpServer({ name: "projectpulse-mcp", version: "0.1.0" });
11
12
  registerCheckCiStatus(server);
12
13
  registerGetRepoHealth(server);
@@ -14,6 +15,7 @@ registerAnalyzeDependencies(server);
14
15
  registerAnalyzeCodeScanning(server);
15
16
  registerGetHealthScore(server);
16
17
  registerGetDoraMetrics(server);
18
+ registerCompareRepos(server);
17
19
  server.registerTool("ping", {
18
20
  description: "This is a simple ping, response with pong",
19
21
  inputSchema: z.object({
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AAE7E,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,2BAA2B,CAAC,MAAM,CAAC,CAAC;AACpC,2BAA2B,CAAC,MAAM,CAAC,CAAC;AACpC,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC/B,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAE/B,MAAM,CAAC,YAAY,CACf,MAAM,EAAE;IACR,WAAW,EAAE,2CAA2C;IACxD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QAClB,OAAO,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,CAAC,2CAA2C,CAAC;KAC7D,CAAC;CACL,EACG,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IAClB,OAAO;QACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,OAAO,EAAE,EAAE,CAAC;KACvD,CAAC;AACN,CAAC,CACJ,CAAA;AAED,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;AAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AAE7E,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,2BAA2B,CAAC,MAAM,CAAC,CAAC;AACpC,2BAA2B,CAAC,MAAM,CAAC,CAAC;AACpC,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC/B,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC/B,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAE7B,MAAM,CAAC,YAAY,CACf,MAAM,EAAE;IACR,WAAW,EAAE,2CAA2C;IACxD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QAClB,OAAO,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,CAAC,2CAA2C,CAAC;KAC7D,CAAC;CACL,EACG,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IAClB,OAAO;QACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,OAAO,EAAE,EAAE,CAAC;KACvD,CAAC;AACN,CAAC,CACJ,CAAA;AAED,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;AAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare const inputSchema: z.ZodObject<{
4
+ repos: z.ZodArray<z.ZodObject<{
5
+ owner: z.ZodString;
6
+ repo: z.ZodString;
7
+ }, z.core.$strip>>;
8
+ }, z.core.$strip>;
9
+ export declare function executeCompareRepos({ repos }: {
10
+ repos: {
11
+ owner: string;
12
+ repo: string;
13
+ }[];
14
+ }): Promise<{
15
+ content: {
16
+ type: "text";
17
+ text: string;
18
+ }[];
19
+ }>;
20
+ export declare function registerCompareRepos(server: McpServer): void;
21
+ //# sourceMappingURL=compare-repos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare-repos.d.ts","sourceRoot":"","sources":["../../src/tools/compare-repos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,eAAO,MAAM,WAAW;;;;;iBAWtB,CAAC;AAEH,wBAAsB,mBAAmB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE,EAAE,CAAA;CAAE;;;;;GAoEjG;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAS5D"}
@@ -0,0 +1,81 @@
1
+ import { z } from "zod";
2
+ import { executeGetHealthScore } from "./get-health-score.js";
3
+ export const inputSchema = z.object({
4
+ repos: z.array(z.object({
5
+ owner: z.string().describe("GitHub repository owner"),
6
+ repo: z.string().describe("GitHub repository name")
7
+ })).min(2, {
8
+ message: "At least 2 repositories are required"
9
+ }).max(5, {
10
+ message: "At most 5 repositories are allowed"
11
+ })
12
+ });
13
+ export async function executeCompareRepos({ repos }) {
14
+ const reposResults = await Promise.all(repos.map(async (repo) => {
15
+ try {
16
+ const result = await executeGetHealthScore(repo);
17
+ const report = JSON.parse(result.content[0].text);
18
+ return {
19
+ owner: repo.owner,
20
+ repo: repo.repo,
21
+ health: report,
22
+ error: null,
23
+ };
24
+ }
25
+ catch (error) {
26
+ return {
27
+ owner: repo.owner,
28
+ repo: repo.repo,
29
+ health: null,
30
+ error: error.message || String(error),
31
+ };
32
+ }
33
+ }));
34
+ const successes = reposResults
35
+ .filter((r) => r.health !== null)
36
+ .sort((a, b) => b.health.score - a.health.score);
37
+ const failures = reposResults
38
+ .filter(r => r.health === null)
39
+ .map(r => ({
40
+ owner: r.owner,
41
+ repo: r.repo,
42
+ rank: 0,
43
+ health: null,
44
+ error: r.error
45
+ }));
46
+ const rankedSuccesses = [];
47
+ let currentRank = 1;
48
+ for (let i = 0; i < successes.length; i++) {
49
+ const current = successes[i];
50
+ if (i > 0) {
51
+ const prev = successes[i - 1];
52
+ if (current.health.score < prev.health.score) {
53
+ currentRank = i + 1;
54
+ }
55
+ }
56
+ rankedSuccesses.push({
57
+ owner: current.owner,
58
+ repo: current.repo,
59
+ rank: currentRank,
60
+ health: current.health,
61
+ error: null
62
+ });
63
+ }
64
+ const comparedRepos = [...rankedSuccesses, ...failures];
65
+ const comparison = {
66
+ compared_at: new Date().toISOString(),
67
+ repos: comparedRepos
68
+ };
69
+ return {
70
+ content: [
71
+ { type: "text", text: JSON.stringify(comparison, null, 2) }
72
+ ]
73
+ };
74
+ }
75
+ export function registerCompareRepos(server) {
76
+ server.registerTool("compare_repos", {
77
+ description: "Compare health scores of multiple GitHub repositories, returning ranked JSON with category breakdown and ranking for each repository",
78
+ inputSchema,
79
+ }, executeCompareRepos);
80
+ }
81
+ //# sourceMappingURL=compare-repos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare-repos.js","sourceRoot":"","sources":["../../src/tools/compare-repos.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=compare-repos.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare-repos.test.d.ts","sourceRoot":"","sources":["../../src/tools/compare-repos.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,138 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { executeCompareRepos, inputSchema } from './compare-repos.js';
3
+ import { executeGetHealthScore } from './get-health-score.js';
4
+ vi.mock('./get-health-score.js');
5
+ describe('executeCompareRepos', () => {
6
+ beforeEach(() => {
7
+ vi.resetAllMocks();
8
+ });
9
+ it('should retrieve scores, rank repositories correctly including ties, and handle failures gracefully', async () => {
10
+ // Mock executeGetHealthScore behavior:
11
+ // repo-a: 85 score
12
+ // repo-b: 85 score (tie with repo-a)
13
+ // repo-c: 70 score (should be rank 3 in standard competition ranking)
14
+ // repo-fail: fails
15
+ vi.mocked(executeGetHealthScore).mockImplementation(async (repo) => {
16
+ if (repo.repo === 'repo-a') {
17
+ return {
18
+ content: [{
19
+ type: 'text',
20
+ text: JSON.stringify({
21
+ repo: 'test/repo-a',
22
+ score: 85,
23
+ grade: 'B',
24
+ breakdown: {},
25
+ suggestions: [],
26
+ checkedAt: '2023-10-01T00:00:00Z'
27
+ })
28
+ }]
29
+ };
30
+ }
31
+ if (repo.repo === 'repo-b') {
32
+ return {
33
+ content: [{
34
+ type: 'text',
35
+ text: JSON.stringify({
36
+ repo: 'test/repo-b',
37
+ score: 85,
38
+ grade: 'B',
39
+ breakdown: {},
40
+ suggestions: [],
41
+ checkedAt: '2023-10-01T00:00:00Z'
42
+ })
43
+ }]
44
+ };
45
+ }
46
+ if (repo.repo === 'repo-c') {
47
+ return {
48
+ content: [{
49
+ type: 'text',
50
+ text: JSON.stringify({
51
+ repo: 'test/repo-c',
52
+ score: 70,
53
+ grade: 'C',
54
+ breakdown: {},
55
+ suggestions: [],
56
+ checkedAt: '2023-10-01T00:00:00Z'
57
+ })
58
+ }]
59
+ };
60
+ }
61
+ throw new Error('API Rate Limit or Repository not found');
62
+ });
63
+ const repos = [
64
+ { owner: 'test', repo: 'repo-a' },
65
+ { owner: 'test', repo: 'repo-fail' },
66
+ { owner: 'test', repo: 'repo-b' },
67
+ { owner: 'test', repo: 'repo-c' }
68
+ ];
69
+ const result = await executeCompareRepos({ repos });
70
+ expect(result.content).toHaveLength(1);
71
+ expect(result.content[0].type).toBe('text');
72
+ const comparison = JSON.parse(result.content[0].text);
73
+ expect(comparison.compared_at).toBeDefined();
74
+ expect(comparison.repos).toHaveLength(4);
75
+ // Verify sorting order: success ordered by score desc, then failures
76
+ expect(comparison.repos[0].repo).toMatch(/repo-a|repo-b/);
77
+ expect(comparison.repos[1].repo).toMatch(/repo-a|repo-b/);
78
+ expect(comparison.repos[2].repo).toBe('repo-c');
79
+ expect(comparison.repos[3].repo).toBe('repo-fail');
80
+ const repoA = comparison.repos.find((r) => r.repo === 'repo-a');
81
+ const repoB = comparison.repos.find((r) => r.repo === 'repo-b');
82
+ const repoC = comparison.repos.find((r) => r.repo === 'repo-c');
83
+ const repoFail = comparison.repos.find((r) => r.repo === 'repo-fail');
84
+ expect(repoA.rank).toBe(1);
85
+ expect(repoA.health.score).toBe(85);
86
+ expect(repoA.error).toBeNull();
87
+ expect(repoB.rank).toBe(1);
88
+ expect(repoB.health.score).toBe(85);
89
+ expect(repoB.error).toBeNull();
90
+ // repo-c should be rank 3 (since 2 repos share rank 1)
91
+ expect(repoC.rank).toBe(3);
92
+ expect(repoC.health.score).toBe(70);
93
+ expect(repoC.error).toBeNull();
94
+ // repo-fail should be rank 0 with error details
95
+ expect(repoFail.rank).toBe(0);
96
+ expect(repoFail.health).toBeNull();
97
+ expect(repoFail.error).toContain('API Rate Limit');
98
+ });
99
+ describe('inputSchema validation', () => {
100
+ it('should validate correctly with 2 to 5 repos', () => {
101
+ const valid = inputSchema.safeParse({
102
+ repos: [
103
+ { owner: 'owner1', repo: 'repo1' },
104
+ { owner: 'owner2', repo: 'repo2' }
105
+ ]
106
+ });
107
+ expect(valid.success).toBe(true);
108
+ });
109
+ it('should fail validation with less than 2 repos', () => {
110
+ const invalid = inputSchema.safeParse({
111
+ repos: [
112
+ { owner: 'owner1', repo: 'repo1' }
113
+ ]
114
+ });
115
+ expect(invalid.success).toBe(false);
116
+ if (!invalid.success) {
117
+ expect(invalid.error.issues[0].message).toBe('At least 2 repositories are required');
118
+ }
119
+ });
120
+ it('should fail validation with more than 5 repos', () => {
121
+ const invalid = inputSchema.safeParse({
122
+ repos: [
123
+ { owner: 'owner1', repo: 'repo1' },
124
+ { owner: 'owner2', repo: 'repo2' },
125
+ { owner: 'owner3', repo: 'repo3' },
126
+ { owner: 'owner4', repo: 'repo4' },
127
+ { owner: 'owner5', repo: 'repo5' },
128
+ { owner: 'owner6', repo: 'repo6' }
129
+ ]
130
+ });
131
+ expect(invalid.success).toBe(false);
132
+ if (!invalid.success) {
133
+ expect(invalid.error.issues[0].message).toBe('At most 5 repositories are allowed');
134
+ }
135
+ });
136
+ });
137
+ });
138
+ //# sourceMappingURL=compare-repos.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare-repos.test.js","sourceRoot":"","sources":["../../src/tools/compare-repos.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "projectpulse-mcp",
3
3
  "description": "MCP Server for GitHub repository health monitoring",
4
- "version": "1.3.0",
4
+ "version": "1.4.0",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
7
7
  "test": "vitest run",