projectpulse-mcp 1.6.14 → 1.8.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
@@ -11,6 +11,7 @@
11
11
  <a href="https://github.com/alexbypa/github-projectpulse-mcp/stargazers"><img src="https://img.shields.io/github/stars/alexbypa/github-projectpulse-mcp" alt="stars" /></a>
12
12
  <a href="https://www.npmjs.com/package/projectpulse-mcp"><img src="https://img.shields.io/npm/dt/projectpulse-mcp" alt="downloads" /></a>
13
13
  <a href="https://scorecard.dev/viewer/?uri=github.com/alexbypa/github-projectpulse-mcp"><img src="https://img.shields.io/ossf-scorecard/github.com/alexbypa/github-projectpulse-mcp?label=openssf+scorecard" alt="OpenSSF Scorecard" /></a>
14
+ <a href="https://m8ven.ai/badge/mcp/alexbypa/github-projectpulse-mcp"><img src="https://m8ven.ai/badge/mcp/alexbypa/github-projectpulse-mcp" alt="M8ven MCP" /></a>
14
15
  </p>
15
16
 
16
17
  ---
@@ -26,6 +27,7 @@ This [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server giv
26
27
  - 📦 **Dependency Analysis** — Dependabot alerts with severity filtering
27
28
  - ⚙️ **CI/CD Status** — recent GitHub Actions workflow runs and conclusions
28
29
  - 📋 **Repository Info** — stars, forks, language, license, and general metadata
30
+ - 📝 **Best Practices** — 12-point checklist for open-source repo quality (README, LICENSE, SECURITY.md, templates, branch protection, etc.) with A-F grade
29
31
 
30
32
  ## 📸 Examples
31
33
 
@@ -67,6 +69,12 @@ This [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server giv
67
69
  ![analyze_dependencies](https://raw.githubusercontent.com/alexbypa/github-projectpulse-mcp/main/docs/images/analyze_dependencies_Use_Case.png)
68
70
  </details>
69
71
 
72
+ <details>
73
+ <summary><strong>check_best_practices</strong> — Open-source repo quality checklist with A-F grade</summary>
74
+
75
+ ![check_best_practices](https://raw.githubusercontent.com/alexbypa/github-projectpulse-mcp/main/docs/images/check_best_practices_Use_Case.png)
76
+ </details>
77
+
70
78
  ## 💡 Use Cases
71
79
 
72
80
  > Copy-paste these prompts into Claude, Cursor, Windsurf, or any MCP-compatible assistant.
@@ -214,6 +222,12 @@ Lists **CodeQL and other code scanning alerts**: rule ID, severity, vulnerabilit
214
222
  **Inputs:** `owner`, `repo`, `trigger_scan` (optional, default `false`), `poll_timeout_seconds` (optional, default 300), `poll_interval_seconds` (optional, default 15)
215
223
  **Try asking:** *"Are there any code scanning vulnerabilities in my-org/my-api?"*
216
224
 
225
+ ### `check_best_practices`
226
+ Evaluates whether a repository follows **open-source community best practices**. Runs 12 weighted checks: README, LICENSE, SECURITY.md, CONTRIBUTING.md, CODE_OF_CONDUCT, issue/PR templates, branch protection, Dependabot config, CI/CD workflows, description, and topics. Returns a 0-100 weighted score with A-F grade, per-check pass/fail/unknown status, and actionable suggestions for every missing item. Branch protection check requires admin token access — returns "unknown" (not penalized) if unavailable.
227
+
228
+ **Inputs:** `owner`, `repo`
229
+ **Try asking:** *"Does my-org/my-repo follow open-source best practices?"*
230
+
217
231
  ### `ping`
218
232
  Simple connectivity check. Returns "pong" with your message. Use to verify the MCP server is running.
219
233
 
@@ -221,6 +235,37 @@ Simple connectivity check. Returns "pong" with your message. Use to verify the M
221
235
 
222
236
  ## 🆕 What's New
223
237
 
238
+ ### HTTP Transport (v1.7.0)
239
+
240
+ ProjectPulse now supports **Streamable HTTP transport** in addition to stdio. This enables running as a standalone HTTP server — ideal for Docker containers, remote deployments, and cross-runtime integrations (e.g., .NET clients consuming Node.js MCP tools over the network).
241
+
242
+ **Default behavior is unchanged** — `npx projectpulse-mcp` still uses stdio. To activate HTTP mode:
243
+
244
+ ```bash
245
+ MCP_TRANSPORT=http MCP_PORT=3000 node dist/index.js
246
+ ```
247
+
248
+ | Env Variable | Default | Description |
249
+ | --- | --- | --- |
250
+ | `MCP_TRANSPORT` | `stdio` | Transport mode: `stdio` or `http` |
251
+ | `MCP_PORT` | `3000` | HTTP server port (only used when `MCP_TRANSPORT=http`) |
252
+
253
+ **Docker example:**
254
+
255
+ ```yaml
256
+ projectpulse:
257
+ image: node:22-slim
258
+ command: >-
259
+ sh -c "npm install -g projectpulse-mcp &&
260
+ node /usr/local/lib/node_modules/projectpulse-mcp/dist/index.js"
261
+ environment:
262
+ - MCP_TRANSPORT=http
263
+ - MCP_PORT=3000
264
+ - GITHUB_TOKEN=${GITHUB_TOKEN}
265
+ ```
266
+
267
+ Health check endpoint available at `GET /` (returns JSON with server name and version). MCP protocol endpoint at `POST /mcp`.
268
+
224
269
  ### OpenSSF Scorecard Integration
225
270
 
226
271
  Security score now blends **Dependabot alerts** (60%) with **OpenSSF Scorecard** checks (40%) for a more complete picture. 12 security-relevant checks are evaluated — repos without a scorecard gracefully fall back to Dependabot-only scoring.
@@ -0,0 +1,34 @@
1
+ import type { BestPracticesReport } from '../types/best-practices.js';
2
+ type BestPracticesInput = {
3
+ owner: string;
4
+ repo: string;
5
+ repoData: {
6
+ description: string | null;
7
+ license: {
8
+ spdx_id: string;
9
+ } | null;
10
+ topics: string[];
11
+ default_branch: string;
12
+ };
13
+ communityProfile: {
14
+ files: {
15
+ code_of_conduct: unknown | null;
16
+ contributing: unknown | null;
17
+ security: unknown | null;
18
+ pull_request_template: unknown | null;
19
+ issue_template: unknown | null;
20
+ };
21
+ };
22
+ readme: {
23
+ found: boolean;
24
+ size: number;
25
+ };
26
+ dependabotConfigured: boolean;
27
+ hasWorkflows: boolean;
28
+ branchProtection: {
29
+ protected: boolean | null;
30
+ };
31
+ };
32
+ export declare function analyzeBestPractices(input: BestPracticesInput): BestPracticesReport;
33
+ export type { BestPracticesInput };
34
+ //# sourceMappingURL=best-practices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"best-practices.d.ts","sourceRoot":"","sources":["../../src/analytics/best-practices.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,mBAAmB,EAAe,MAAM,4BAA4B,CAAC;AAItG,KAAK,kBAAkB,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QACR,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,OAAO,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;QACpC,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,gBAAgB,EAAE;QAChB,KAAK,EAAE;YACL,eAAe,EAAE,OAAO,GAAG,IAAI,CAAC;YAChC,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;YAC7B,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;YACzB,qBAAqB,EAAE,OAAO,GAAG,IAAI,CAAC;YACtC,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;SAChC,CAAC;KACH,CAAC;IACF,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB,EAAE;QAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC;CACjD,CAAC;AAMF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,mBAAmB,CAuEnF;AAED,YAAY,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { scoreToGrade } from '../scoring/health-score.js';
2
+ function check(name, status, weight, detail) {
3
+ return { name, status, weight, detail };
4
+ }
5
+ export function analyzeBestPractices(input) {
6
+ const { repoData, communityProfile, readme, dependabotConfigured, hasWorkflows, branchProtection } = input;
7
+ const files = communityProfile.files;
8
+ const checks = [
9
+ // Alto (weight 3)
10
+ check('README', readme.found && readme.size > 0 ? 'pass' : 'fail', 3, readme.found ? (readme.size > 0 ? 'README present and not empty' : 'README exists but is empty') : 'No README found'),
11
+ check('LICENSE', repoData.license != null ? 'pass' : 'fail', 3, repoData.license ? `License: ${repoData.license.spdx_id}` : 'No license — code is not legally reusable'),
12
+ check('Security Policy', files.security != null ? 'pass' : 'fail', 3, files.security ? 'SECURITY.md or security policy present' : 'No security policy — vulnerability reporters have no contact channel'),
13
+ check('Branch Protection', branchProtection.protected == null ? 'unknown' : (branchProtection.protected ? 'pass' : 'fail'), 3, branchProtection.protected == null ? 'Cannot verify — requires admin access'
14
+ : (branchProtection.protected ? 'Default branch is protected' : 'Default branch has no protection — anyone can push directly')),
15
+ check('Dependabot', dependabotConfigured ? 'pass' : 'fail', 3, dependabotConfigured ? '.github/dependabot.yml configured' : 'No Dependabot config — dependency vulnerabilities not auto-detected'),
16
+ check('CI/CD', hasWorkflows ? 'pass' : 'fail', 3, hasWorkflows ? 'GitHub Actions workflows configured' : 'No CI/CD workflows found'),
17
+ // Medio (weight 2)
18
+ check('CONTRIBUTING', files.contributing != null ? 'pass' : 'fail', 2, files.contributing ? 'CONTRIBUTING.md present' : 'No contributing guide — new contributors lack onboarding'),
19
+ check('Issue Templates', files.issue_template != null ? 'pass' : 'fail', 2, files.issue_template ? 'Issue templates configured' : 'No issue templates — bug reports will lack structure'),
20
+ check('PR Template', files.pull_request_template != null ? 'pass' : 'fail', 2, files.pull_request_template ? 'Pull request template configured' : 'No PR template'),
21
+ check('Description', (repoData.description ?? '').length > 0 ? 'pass' : 'fail', 2, repoData.description ? 'Repository description set' : 'No description — repo is invisible in search'),
22
+ // Basso (weight 1)
23
+ check('Code of Conduct', files.code_of_conduct != null ? 'pass' : 'fail', 1, files.code_of_conduct ? 'Code of conduct present' : 'No code of conduct'),
24
+ check('Topics', repoData.topics.length > 0 ? 'pass' : 'fail', 1, repoData.topics.length > 0 ? `${repoData.topics.length} topics configured` : 'No topics — reduces discoverability'),
25
+ ];
26
+ // Score pesato: solo pass e fail contano, unknown esclusi
27
+ const scorable = checks.filter(c => c.status !== 'unknown');
28
+ const totalWeight = scorable.reduce((sum, c) => sum + c.weight, 0);
29
+ const passedWeight = scorable.filter(c => c.status === 'pass').reduce((sum, c) => sum + c.weight, 0);
30
+ const score = totalWeight > 0 ? Math.round((passedWeight / totalWeight) * 100) : 0;
31
+ // Suggestions per i fail, ordinati per weight decrescente
32
+ const suggestions = checks
33
+ .filter(c => c.status === 'fail')
34
+ .sort((a, b) => b.weight - a.weight)
35
+ .map(c => `${c.name}: ${c.detail}`);
36
+ return {
37
+ repo: `${input.owner}/${input.repo}`,
38
+ score,
39
+ grade: scoreToGrade(score),
40
+ totalChecks: checks.length,
41
+ passed: checks.filter(c => c.status === 'pass').length,
42
+ failed: checks.filter(c => c.status === 'fail').length,
43
+ unknown: checks.filter(c => c.status === 'unknown').length,
44
+ checks,
45
+ suggestions,
46
+ checkedAt: new Date().toISOString(),
47
+ };
48
+ }
49
+ //# sourceMappingURL=best-practices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"best-practices.js","sourceRoot":"","sources":["../../src/analytics/best-practices.ts"],"names":[],"mappings":"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"}
@@ -4,4 +4,28 @@ export declare function getOctokit(): Octokit;
4
4
  export declare function listReleases(owner: string, repo: string, since: Date): Promise<Release[]>;
5
5
  export declare function listMergedPulls(owner: string, repo: string, since: Date): Promise<PullRequest[]>;
6
6
  export declare function listWorkflowRuns(owner: string, repo: string, since: Date): Promise<WorkflowRun[]>;
7
+ export declare function getCommunityProfile(owner: string, repo: string): Promise<{
8
+ health_percentage: number;
9
+ description: string | null;
10
+ documentation: string | null;
11
+ files: {
12
+ code_of_conduct: import("@octokit/openapi-types").components["schemas"]["nullable-code-of-conduct-simple"];
13
+ code_of_conduct_file: import("@octokit/openapi-types").components["schemas"]["nullable-community-health-file"];
14
+ license: import("@octokit/openapi-types").components["schemas"]["nullable-license-simple"];
15
+ contributing: import("@octokit/openapi-types").components["schemas"]["nullable-community-health-file"];
16
+ readme: import("@octokit/openapi-types").components["schemas"]["nullable-community-health-file"];
17
+ issue_template: import("@octokit/openapi-types").components["schemas"]["nullable-community-health-file"];
18
+ pull_request_template: import("@octokit/openapi-types").components["schemas"]["nullable-community-health-file"];
19
+ };
20
+ updated_at: string | null;
21
+ content_reports_enabled?: boolean;
22
+ }>;
23
+ export declare function getReadme(owner: string, repo: string): Promise<{
24
+ found: boolean;
25
+ size: number;
26
+ }>;
27
+ export declare function getFileExists(owner: string, repo: string, path: string): Promise<boolean>;
28
+ export declare function getBranchProtection(owner: string, repo: string, branch: string): Promise<{
29
+ protected: boolean | null;
30
+ }>;
7
31
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/github/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAI/E,wBAAgB,UAAU,IAAI,OAAO,CAMpC;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAc/F;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAiBtG;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAgBvG"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/github/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAI/E,wBAAgB,UAAU,IAAI,OAAO,CAMpC;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAc/F;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAiBtG;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAgBvG;AAID,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;;;;;yBAwCk6pnC,sDAAsB;8BAAkE,sDAAsB;iBAAoD,sDAAsB;sBAAkD,sDAAsB;gBAAmD,sDAAsB;wBAA2D,sDAAsB;+BAAkE,sDAAsB;;;;GAjCx9qnC;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAQtG;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ/F;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC,CAW7H"}
@@ -57,4 +57,47 @@ export async function listWorkflowRuns(owner, repo, since) {
57
57
  created_at: r.created_at,
58
58
  }));
59
59
  }
60
+ // --- Best Practices ---
61
+ export async function getCommunityProfile(owner, repo) {
62
+ const octokit = getOctokit();
63
+ const { data } = await octokit.request('GET /repos/{owner}/{repo}/community/profile', {
64
+ owner,
65
+ repo,
66
+ });
67
+ return data;
68
+ }
69
+ export async function getReadme(owner, repo) {
70
+ const octokit = getOctokit();
71
+ try {
72
+ const { data } = await octokit.repos.getReadme({ owner, repo });
73
+ return { found: true, size: data.size };
74
+ }
75
+ catch {
76
+ return { found: false, size: 0 };
77
+ }
78
+ }
79
+ export async function getFileExists(owner, repo, path) {
80
+ const octokit = getOctokit();
81
+ try {
82
+ await octokit.repos.getContent({ owner, repo, path });
83
+ return true;
84
+ }
85
+ catch {
86
+ return false;
87
+ }
88
+ }
89
+ export async function getBranchProtection(owner, repo, branch) {
90
+ const octokit = getOctokit();
91
+ try {
92
+ await octokit.repos.getBranchProtection({ owner, repo, branch });
93
+ return { protected: true };
94
+ }
95
+ catch (err) {
96
+ const status = err.status;
97
+ if (status === 404)
98
+ return { protected: false };
99
+ // 403 = no admin access, can't check
100
+ return { protected: null };
101
+ }
102
+ }
60
103
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/github/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,IAAI,eAAe,GAAmB,IAAI,CAAC;AAE3C,MAAM,UAAU,UAAU;IACtB,IAAI,CAAC,eAAe,EAAE,CAAC;QACnB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QACvC,eAAe,GAAG,IAAI,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,eAAe,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa,EAAE,IAAY,EAAE,KAAW;IACvE,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC;QAC9C,KAAK;QACL,IAAI;QACJ,QAAQ,EAAE,GAAG;KAChB,CAAC,CAAC;IACH,OAAO,IAAI;SACN,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC;SACzE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACb,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,YAAY,EAAE,CAAC,CAAC,YAAa;KAChC,CAAC,CAAC,CAAC;AACR,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAa,EAAE,IAAY,EAAE,KAAW;IAC1E,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QACtC,KAAK;QACL,IAAI;QACJ,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,MAAM;QACjB,QAAQ,EAAE,GAAG;KAChB,CAAC,CAAC;IACH,OAAO,IAAI;SACN,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC;SACnE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACb,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,SAAS,EAAE,CAAC,CAAC,SAAS;KACzB,CAAC,CAAC,CAAC;AACR,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAa,EAAE,IAAY,EAAE,KAAW;IAC3E,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,uBAAuB,CAAC;QAC3D,KAAK;QACL,IAAI;QACJ,QAAQ,EAAE,GAAG;KAChB,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,aAAa;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC;SAC5C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACb,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,SAAS;QACzB,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,SAAS;QACvC,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,UAAU,EAAE,CAAC,CAAC,UAAU;KAC3B,CAAC,CAAC,CAAC;AACR,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/github/client.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,5 +1,9 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
5
+ import { createServer } from "node:http";
6
+ import { randomUUID } from "node:crypto";
3
7
  import { registerCheckCiStatus } from "./tools/check-ci-status.js";
4
8
  import { registerGetRepoHealth } from "./tools/get-repo-health.js";
5
9
  import { registerAnalyzeDependencies } from "./tools/analyze-dependencies.js";
@@ -7,19 +11,109 @@ import { registerAnalyzeCodeScanning } from "./tools/analyze-code-scanning.js";
7
11
  import { registerGetHealthScore } from "./tools/get-health-score.js";
8
12
  import { registerGetDoraMetrics } from "./tools/get-dora-metrics.js";
9
13
  import { registerCompareRepos } from "./tools/compare-repos.js";
14
+ import { registerCheckBestPractices } from "./tools/check-best-practices.js";
10
15
  import { readFileSync } from "fs";
11
16
  import { join, dirname } from "path";
12
17
  import { fileURLToPath } from "url";
13
18
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
19
  const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
15
- const server = new McpServer({ name: "projectpulse-mcp", version: pkg.version });
16
- registerCheckCiStatus(server);
17
- registerGetRepoHealth(server);
18
- registerAnalyzeDependencies(server);
19
- registerAnalyzeCodeScanning(server);
20
- registerGetHealthScore(server);
21
- registerGetDoraMetrics(server);
22
- registerCompareRepos(server);
23
- const transport = new StdioServerTransport();
24
- await server.connect(transport);
20
+ function createMcpServer() {
21
+ const server = new McpServer({ name: "projectpulse-mcp", version: pkg.version });
22
+ registerCheckCiStatus(server);
23
+ registerGetRepoHealth(server);
24
+ registerAnalyzeDependencies(server);
25
+ registerAnalyzeCodeScanning(server);
26
+ registerGetHealthScore(server);
27
+ registerGetDoraMetrics(server);
28
+ registerCompareRepos(server);
29
+ registerCheckBestPractices(server);
30
+ return server;
31
+ }
32
+ // --- Transport selection ---
33
+ // Default: stdio (backward compatible, per uso locale e npx)
34
+ // MCP_TRANSPORT=http: Streamable HTTP server (per Docker e remote clients)
35
+ const transportMode = (process.env.MCP_TRANSPORT ?? "stdio").toLowerCase();
36
+ if (transportMode === "http") {
37
+ const port = parseInt(process.env.MCP_PORT ?? "3000", 10);
38
+ const transports = new Map();
39
+ const httpServer = createServer(async (req, res) => {
40
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
41
+ // Only handle /mcp path — MCP protocol endpoint
42
+ if (url.pathname !== "/mcp") {
43
+ // Health check on root
44
+ if (url.pathname === "/" && req.method === "GET") {
45
+ res.writeHead(200, { "Content-Type": "application/json" });
46
+ res.end(JSON.stringify({ status: "ok", server: "projectpulse-mcp", version: pkg.version }));
47
+ return;
48
+ }
49
+ res.writeHead(404);
50
+ res.end("Not Found");
51
+ return;
52
+ }
53
+ // Parse JSON body for POST requests
54
+ if (req.method === "POST") {
55
+ const chunks = [];
56
+ for await (const chunk of req)
57
+ chunks.push(chunk);
58
+ const body = JSON.parse(Buffer.concat(chunks).toString());
59
+ req.body = body;
60
+ const sessionId = req.headers["mcp-session-id"];
61
+ if (sessionId && transports.has(sessionId)) {
62
+ // Existing session — reuse transport
63
+ await transports.get(sessionId).handleRequest(req, res, body);
64
+ }
65
+ else if (!sessionId && isInitializeRequest(body)) {
66
+ // New session — create transport + server
67
+ const transport = new StreamableHTTPServerTransport({
68
+ sessionIdGenerator: () => randomUUID(),
69
+ onsessioninitialized: (sid) => {
70
+ transports.set(sid, transport);
71
+ },
72
+ });
73
+ transport.onclose = () => {
74
+ const sid = transport.sessionId;
75
+ if (sid)
76
+ transports.delete(sid);
77
+ };
78
+ const server = createMcpServer();
79
+ await server.connect(transport);
80
+ await transport.handleRequest(req, res, body);
81
+ }
82
+ else {
83
+ res.writeHead(400, { "Content-Type": "application/json" });
84
+ res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request: No valid session ID" }, id: null }));
85
+ }
86
+ }
87
+ else if (req.method === "GET" || req.method === "DELETE") {
88
+ const sessionId = req.headers["mcp-session-id"];
89
+ if (!sessionId || !transports.has(sessionId)) {
90
+ res.writeHead(400);
91
+ res.end("Invalid or missing session ID");
92
+ return;
93
+ }
94
+ await transports.get(sessionId).handleRequest(req, res);
95
+ }
96
+ else {
97
+ res.writeHead(405);
98
+ res.end("Method Not Allowed");
99
+ }
100
+ });
101
+ httpServer.listen(port, "0.0.0.0", () => {
102
+ console.log(`ProjectPulse MCP server (HTTP) listening on http://0.0.0.0:${port}/mcp`);
103
+ });
104
+ process.on("SIGINT", async () => {
105
+ for (const [, transport] of transports) {
106
+ await transport.close();
107
+ }
108
+ transports.clear();
109
+ httpServer.close();
110
+ process.exit(0);
111
+ });
112
+ }
113
+ else {
114
+ // Default: stdio transport (backward compatible)
115
+ const server = createMcpServer();
116
+ const transport = new StdioServerTransport();
117
+ await server.connect(transport);
118
+ }
25
119
  //# sourceMappingURL=index.js.map
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;AAEhF,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,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAElF,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;AAEjF,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,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":"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"}
@@ -0,0 +1,19 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function executeCheckBestPractices({ owner, repo }: {
3
+ owner: string;
4
+ repo: string;
5
+ }): Promise<{
6
+ content: {
7
+ type: "text";
8
+ text: string;
9
+ }[];
10
+ structuredContent: import("../types/best-practices.js").BestPracticesReport;
11
+ } | {
12
+ content: {
13
+ type: "text";
14
+ text: string;
15
+ }[];
16
+ structuredContent?: undefined;
17
+ }>;
18
+ export declare function registerCheckBestPractices(server: McpServer): void;
19
+ //# sourceMappingURL=check-best-practices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-best-practices.d.ts","sourceRoot":"","sources":["../../src/tools/check-best-practices.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2BpE,wBAAsB,yBAAyB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;GA+D/F;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsBlE"}
@@ -0,0 +1,104 @@
1
+ import { z } from "zod";
2
+ import { getOctokit, getCommunityProfile, getReadme, getFileExists, getBranchProtection } from "../github/client.js";
3
+ import { analyzeBestPractices } from "../analytics/best-practices.js";
4
+ const inputSchema = z.object({
5
+ owner: z.string().describe("GitHub repository owner (e.g., 'facebook')"),
6
+ repo: z.string().describe("GitHub repository name (e.g., 'react')")
7
+ });
8
+ const outputSchema = z.object({
9
+ repo: z.string(),
10
+ score: z.number(),
11
+ grade: z.string(),
12
+ totalChecks: z.number(),
13
+ passed: z.number(),
14
+ failed: z.number(),
15
+ unknown: z.number(),
16
+ checks: z.array(z.object({
17
+ name: z.string(),
18
+ status: z.enum(['pass', 'fail', 'unknown']),
19
+ weight: z.number(),
20
+ detail: z.string()
21
+ })),
22
+ suggestions: z.array(z.string()),
23
+ checkedAt: z.string()
24
+ });
25
+ export async function executeCheckBestPractices({ owner, repo }) {
26
+ try {
27
+ const octokit = getOctokit();
28
+ // Fetch tutto in parallelo — 5 call, ~1 secondo totale
29
+ const [repoResponse, communityProfile, readme, dependabotConfigured, workflowsResponse] = await Promise.all([
30
+ octokit.repos.get({ owner, repo }),
31
+ getCommunityProfile(owner, repo),
32
+ getReadme(owner, repo),
33
+ getFileExists(owner, repo, '.github/dependabot.yml'),
34
+ octokit.actions.listWorkflowRunsForRepo({ owner, repo, per_page: 1 })
35
+ ]);
36
+ const repoData = repoResponse.data;
37
+ // Branch protection separata — può fallire con 403
38
+ const branchProtection = await getBranchProtection(owner, repo, repoData.default_branch);
39
+ const result = analyzeBestPractices({
40
+ owner,
41
+ repo,
42
+ repoData: {
43
+ description: repoData.description,
44
+ license: repoData.license ? { spdx_id: repoData.license.spdx_id ?? 'Unknown' } : null,
45
+ topics: repoData.topics ?? [],
46
+ default_branch: repoData.default_branch,
47
+ },
48
+ communityProfile: {
49
+ files: {
50
+ code_of_conduct: communityProfile.files.code_of_conduct,
51
+ contributing: communityProfile.files.contributing,
52
+ security: communityProfile.files.security ?? communityProfile.files.security_advisories,
53
+ pull_request_template: communityProfile.files.pull_request_template,
54
+ issue_template: communityProfile.files.issue_template,
55
+ }
56
+ },
57
+ readme,
58
+ dependabotConfigured,
59
+ hasWorkflows: workflowsResponse.data.total_count > 0,
60
+ branchProtection,
61
+ });
62
+ return {
63
+ content: [{
64
+ type: "text",
65
+ text: JSON.stringify(result, null, 2),
66
+ }],
67
+ structuredContent: result
68
+ };
69
+ }
70
+ catch (error) {
71
+ if (error.status === 404) {
72
+ return {
73
+ content: [{
74
+ type: "text",
75
+ text: JSON.stringify({
76
+ error: "Repository not found",
77
+ details: error.message
78
+ }, null, 2),
79
+ }]
80
+ };
81
+ }
82
+ throw error;
83
+ }
84
+ }
85
+ export function registerCheckBestPractices(server) {
86
+ server.registerTool("check_best_practices", {
87
+ description: `Checks whether a GitHub repository follows open-source community best practices.
88
+ - Evaluates 12 checks: README, LICENSE, SECURITY.md, CONTRIBUTING.md, CODE_OF_CONDUCT, issue/PR templates, branch protection, Dependabot, CI/CD, description, and topics.
89
+ - Returns a weighted 0-100 score with A-F grade, per-check pass/fail/unknown status, and actionable suggestions for missing items.
90
+ - Side effects: None. Read-only operation.
91
+ - Rate limits: ~6 GitHub API calls per invocation.
92
+ - Branch protection check requires admin access — returns "unknown" (not penalized) if token lacks permission.
93
+ - Use this tool to audit repo quality and discoverability. For a computed health grade based on CI/security/activity, use 'get_health_score' instead.`,
94
+ inputSchema,
95
+ outputSchema,
96
+ annotations: {
97
+ readOnlyHint: true,
98
+ destructiveHint: false,
99
+ idempotentHint: true,
100
+ openWorldHint: true
101
+ }
102
+ }, executeCheckBestPractices);
103
+ }
104
+ //# sourceMappingURL=check-best-practices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-best-practices.js","sourceRoot":"","sources":["../../src/tools/check-best-practices.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ type CheckStatus = 'pass' | 'fail' | 'unknown';
2
+ type BestPracticeCheck = {
3
+ name: string;
4
+ status: CheckStatus;
5
+ weight: number;
6
+ detail: string;
7
+ };
8
+ type BestPracticesReport = {
9
+ repo: string;
10
+ score: number;
11
+ grade: string;
12
+ totalChecks: number;
13
+ passed: number;
14
+ failed: number;
15
+ unknown: number;
16
+ checks: BestPracticeCheck[];
17
+ suggestions: string[];
18
+ checkedAt: string;
19
+ };
20
+ export type { CheckStatus, BestPracticeCheck, BestPracticesReport };
21
+ //# sourceMappingURL=best-practices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"best-practices.d.ts","sourceRoot":"","sources":["../../src/types/best-practices.ts"],"names":[],"mappings":"AACA,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAG/C,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAA;AAGD,KAAK,mBAAmB,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=best-practices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"best-practices.js","sourceRoot":"","sources":["../../src/types/best-practices.ts"],"names":[],"mappings":""}
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.6.14",
4
+ "version": "1.8.0",
5
5
  "mcpName": "io.github.alexbypa/github-projectpulse-mcp",
6
6
  "main": "dist/index.js",
7
7
  "scripts": {