projectpulse-mcp 1.6.14 → 1.7.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
@@ -221,6 +221,37 @@ Simple connectivity check. Returns "pong" with your message. Use to verify the M
221
221
 
222
222
  ## 🆕 What's New
223
223
 
224
+ ### HTTP Transport (v1.7.0)
225
+
226
+ 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).
227
+
228
+ **Default behavior is unchanged** — `npx projectpulse-mcp` still uses stdio. To activate HTTP mode:
229
+
230
+ ```bash
231
+ MCP_TRANSPORT=http MCP_PORT=3000 node dist/index.js
232
+ ```
233
+
234
+ | Env Variable | Default | Description |
235
+ | --- | --- | --- |
236
+ | `MCP_TRANSPORT` | `stdio` | Transport mode: `stdio` or `http` |
237
+ | `MCP_PORT` | `3000` | HTTP server port (only used when `MCP_TRANSPORT=http`) |
238
+
239
+ **Docker example:**
240
+
241
+ ```yaml
242
+ projectpulse:
243
+ image: node:22-slim
244
+ command: >-
245
+ sh -c "npm install -g projectpulse-mcp &&
246
+ node /usr/local/lib/node_modules/projectpulse-mcp/dist/index.js"
247
+ environment:
248
+ - MCP_TRANSPORT=http
249
+ - MCP_PORT=3000
250
+ - GITHUB_TOKEN=${GITHUB_TOKEN}
251
+ ```
252
+
253
+ Health check endpoint available at `GET /` (returns JSON with server name and version). MCP protocol endpoint at `POST /mcp`.
254
+
224
255
  ### OpenSSF Scorecard Integration
225
256
 
226
257
  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.
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";
@@ -12,14 +16,102 @@ import { join, dirname } from "path";
12
16
  import { fileURLToPath } from "url";
13
17
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
18
  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);
19
+ function createMcpServer() {
20
+ const server = new McpServer({ name: "projectpulse-mcp", version: pkg.version });
21
+ registerCheckCiStatus(server);
22
+ registerGetRepoHealth(server);
23
+ registerAnalyzeDependencies(server);
24
+ registerAnalyzeCodeScanning(server);
25
+ registerGetHealthScore(server);
26
+ registerGetDoraMetrics(server);
27
+ registerCompareRepos(server);
28
+ return server;
29
+ }
30
+ // --- Transport selection ---
31
+ // Default: stdio (backward compatible, per uso locale e npx)
32
+ // MCP_TRANSPORT=http: Streamable HTTP server (per Docker e remote clients)
33
+ const transportMode = (process.env.MCP_TRANSPORT ?? "stdio").toLowerCase();
34
+ if (transportMode === "http") {
35
+ const port = parseInt(process.env.MCP_PORT ?? "3000", 10);
36
+ const transports = new Map();
37
+ const httpServer = createServer(async (req, res) => {
38
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
39
+ // Only handle /mcp path — MCP protocol endpoint
40
+ if (url.pathname !== "/mcp") {
41
+ // Health check on root
42
+ if (url.pathname === "/" && req.method === "GET") {
43
+ res.writeHead(200, { "Content-Type": "application/json" });
44
+ res.end(JSON.stringify({ status: "ok", server: "projectpulse-mcp", version: pkg.version }));
45
+ return;
46
+ }
47
+ res.writeHead(404);
48
+ res.end("Not Found");
49
+ return;
50
+ }
51
+ // Parse JSON body for POST requests
52
+ if (req.method === "POST") {
53
+ const chunks = [];
54
+ for await (const chunk of req)
55
+ chunks.push(chunk);
56
+ const body = JSON.parse(Buffer.concat(chunks).toString());
57
+ req.body = body;
58
+ const sessionId = req.headers["mcp-session-id"];
59
+ if (sessionId && transports.has(sessionId)) {
60
+ // Existing session — reuse transport
61
+ await transports.get(sessionId).handleRequest(req, res, body);
62
+ }
63
+ else if (!sessionId && isInitializeRequest(body)) {
64
+ // New session — create transport + server
65
+ const transport = new StreamableHTTPServerTransport({
66
+ sessionIdGenerator: () => randomUUID(),
67
+ onsessioninitialized: (sid) => {
68
+ transports.set(sid, transport);
69
+ },
70
+ });
71
+ transport.onclose = () => {
72
+ const sid = transport.sessionId;
73
+ if (sid)
74
+ transports.delete(sid);
75
+ };
76
+ const server = createMcpServer();
77
+ await server.connect(transport);
78
+ await transport.handleRequest(req, res, body);
79
+ }
80
+ else {
81
+ res.writeHead(400, { "Content-Type": "application/json" });
82
+ res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request: No valid session ID" }, id: null }));
83
+ }
84
+ }
85
+ else if (req.method === "GET" || req.method === "DELETE") {
86
+ const sessionId = req.headers["mcp-session-id"];
87
+ if (!sessionId || !transports.has(sessionId)) {
88
+ res.writeHead(400);
89
+ res.end("Invalid or missing session ID");
90
+ return;
91
+ }
92
+ await transports.get(sessionId).handleRequest(req, res);
93
+ }
94
+ else {
95
+ res.writeHead(405);
96
+ res.end("Method Not Allowed");
97
+ }
98
+ });
99
+ httpServer.listen(port, "0.0.0.0", () => {
100
+ console.log(`ProjectPulse MCP server (HTTP) listening on http://0.0.0.0:${port}/mcp`);
101
+ });
102
+ process.on("SIGINT", async () => {
103
+ for (const [, transport] of transports) {
104
+ await transport.close();
105
+ }
106
+ transports.clear();
107
+ httpServer.close();
108
+ process.exit(0);
109
+ });
110
+ }
111
+ else {
112
+ // Default: stdio transport (backward compatible)
113
+ const server = createMcpServer();
114
+ const transport = new StdioServerTransport();
115
+ await server.connect(transport);
116
+ }
25
117
  //# 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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"}
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.7.0",
5
5
  "mcpName": "io.github.alexbypa/github-projectpulse-mcp",
6
6
  "main": "dist/index.js",
7
7
  "scripts": {