mcp-researchpowerpack-http 3.10.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.
Files changed (127) hide show
  1. package/README.md +124 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +227 -0
  5. package/dist/index.js.map +7 -0
  6. package/dist/mcp-use.json +7 -0
  7. package/dist/src/clients/github.d.ts +83 -0
  8. package/dist/src/clients/github.d.ts.map +1 -0
  9. package/dist/src/clients/github.js +370 -0
  10. package/dist/src/clients/github.js.map +7 -0
  11. package/dist/src/clients/reddit.d.ts +60 -0
  12. package/dist/src/clients/reddit.d.ts.map +1 -0
  13. package/dist/src/clients/reddit.js +287 -0
  14. package/dist/src/clients/reddit.js.map +7 -0
  15. package/dist/src/clients/research.d.ts +67 -0
  16. package/dist/src/clients/research.d.ts.map +1 -0
  17. package/dist/src/clients/research.js +282 -0
  18. package/dist/src/clients/research.js.map +7 -0
  19. package/dist/src/clients/scraper.d.ts +72 -0
  20. package/dist/src/clients/scraper.d.ts.map +1 -0
  21. package/dist/src/clients/scraper.js +327 -0
  22. package/dist/src/clients/scraper.js.map +7 -0
  23. package/dist/src/clients/search.d.ts +57 -0
  24. package/dist/src/clients/search.d.ts.map +1 -0
  25. package/dist/src/clients/search.js +218 -0
  26. package/dist/src/clients/search.js.map +7 -0
  27. package/dist/src/config/index.d.ts +93 -0
  28. package/dist/src/config/index.d.ts.map +1 -0
  29. package/dist/src/config/index.js +218 -0
  30. package/dist/src/config/index.js.map +7 -0
  31. package/dist/src/schemas/deep-research.d.ts +40 -0
  32. package/dist/src/schemas/deep-research.d.ts.map +1 -0
  33. package/dist/src/schemas/deep-research.js +216 -0
  34. package/dist/src/schemas/deep-research.js.map +7 -0
  35. package/dist/src/schemas/github-score.d.ts +50 -0
  36. package/dist/src/schemas/github-score.d.ts.map +1 -0
  37. package/dist/src/schemas/github-score.js +58 -0
  38. package/dist/src/schemas/github-score.js.map +7 -0
  39. package/dist/src/schemas/scrape-links.d.ts +23 -0
  40. package/dist/src/schemas/scrape-links.d.ts.map +1 -0
  41. package/dist/src/schemas/scrape-links.js +32 -0
  42. package/dist/src/schemas/scrape-links.js.map +7 -0
  43. package/dist/src/schemas/web-search.d.ts +18 -0
  44. package/dist/src/schemas/web-search.d.ts.map +1 -0
  45. package/dist/src/schemas/web-search.js +28 -0
  46. package/dist/src/schemas/web-search.js.map +7 -0
  47. package/dist/src/scoring/github-quality.d.ts +142 -0
  48. package/dist/src/scoring/github-quality.d.ts.map +1 -0
  49. package/dist/src/scoring/github-quality.js +202 -0
  50. package/dist/src/scoring/github-quality.js.map +7 -0
  51. package/dist/src/services/file-attachment.d.ts +30 -0
  52. package/dist/src/services/file-attachment.d.ts.map +1 -0
  53. package/dist/src/services/file-attachment.js +205 -0
  54. package/dist/src/services/file-attachment.js.map +7 -0
  55. package/dist/src/services/llm-processor.d.ts +29 -0
  56. package/dist/src/services/llm-processor.d.ts.map +1 -0
  57. package/dist/src/services/llm-processor.js +206 -0
  58. package/dist/src/services/llm-processor.js.map +7 -0
  59. package/dist/src/services/markdown-cleaner.d.ts +8 -0
  60. package/dist/src/services/markdown-cleaner.d.ts.map +1 -0
  61. package/dist/src/services/markdown-cleaner.js +63 -0
  62. package/dist/src/services/markdown-cleaner.js.map +7 -0
  63. package/dist/src/tools/github-score.d.ts +12 -0
  64. package/dist/src/tools/github-score.d.ts.map +1 -0
  65. package/dist/src/tools/github-score.js +306 -0
  66. package/dist/src/tools/github-score.js.map +7 -0
  67. package/dist/src/tools/mcp-helpers.d.ts +27 -0
  68. package/dist/src/tools/mcp-helpers.d.ts.map +1 -0
  69. package/dist/src/tools/mcp-helpers.js +47 -0
  70. package/dist/src/tools/mcp-helpers.js.map +7 -0
  71. package/dist/src/tools/reddit.d.ts +54 -0
  72. package/dist/src/tools/reddit.d.ts.map +1 -0
  73. package/dist/src/tools/reddit.js +498 -0
  74. package/dist/src/tools/reddit.js.map +7 -0
  75. package/dist/src/tools/registry.d.ts +3 -0
  76. package/dist/src/tools/registry.d.ts.map +1 -0
  77. package/dist/src/tools/registry.js +17 -0
  78. package/dist/src/tools/registry.js.map +7 -0
  79. package/dist/src/tools/research.d.ts +14 -0
  80. package/dist/src/tools/research.d.ts.map +1 -0
  81. package/dist/src/tools/research.js +250 -0
  82. package/dist/src/tools/research.js.map +7 -0
  83. package/dist/src/tools/scrape.d.ts +14 -0
  84. package/dist/src/tools/scrape.d.ts.map +1 -0
  85. package/dist/src/tools/scrape.js +290 -0
  86. package/dist/src/tools/scrape.js.map +7 -0
  87. package/dist/src/tools/search.d.ts +10 -0
  88. package/dist/src/tools/search.d.ts.map +1 -0
  89. package/dist/src/tools/search.js +197 -0
  90. package/dist/src/tools/search.js.map +7 -0
  91. package/dist/src/tools/utils.d.ts +105 -0
  92. package/dist/src/tools/utils.d.ts.map +1 -0
  93. package/dist/src/tools/utils.js +96 -0
  94. package/dist/src/tools/utils.js.map +7 -0
  95. package/dist/src/utils/concurrency.d.ts +28 -0
  96. package/dist/src/utils/concurrency.d.ts.map +1 -0
  97. package/dist/src/utils/concurrency.js +62 -0
  98. package/dist/src/utils/concurrency.js.map +7 -0
  99. package/dist/src/utils/errors.d.ts +95 -0
  100. package/dist/src/utils/errors.d.ts.map +1 -0
  101. package/dist/src/utils/errors.js +289 -0
  102. package/dist/src/utils/errors.js.map +7 -0
  103. package/dist/src/utils/logger.d.ts +33 -0
  104. package/dist/src/utils/logger.d.ts.map +1 -0
  105. package/dist/src/utils/logger.js +41 -0
  106. package/dist/src/utils/logger.js.map +7 -0
  107. package/dist/src/utils/markdown-formatter.d.ts +5 -0
  108. package/dist/src/utils/markdown-formatter.d.ts.map +1 -0
  109. package/dist/src/utils/markdown-formatter.js +15 -0
  110. package/dist/src/utils/markdown-formatter.js.map +7 -0
  111. package/dist/src/utils/response.d.ts +83 -0
  112. package/dist/src/utils/response.d.ts.map +1 -0
  113. package/dist/src/utils/response.js +109 -0
  114. package/dist/src/utils/response.js.map +7 -0
  115. package/dist/src/utils/retry.d.ts +43 -0
  116. package/dist/src/utils/retry.d.ts.map +1 -0
  117. package/dist/src/utils/retry.js +37 -0
  118. package/dist/src/utils/retry.js.map +7 -0
  119. package/dist/src/utils/url-aggregator.d.ts +92 -0
  120. package/dist/src/utils/url-aggregator.d.ts.map +1 -0
  121. package/dist/src/utils/url-aggregator.js +357 -0
  122. package/dist/src/utils/url-aggregator.js.map +7 -0
  123. package/dist/src/version.d.ts +28 -0
  124. package/dist/src/version.d.ts.map +1 -0
  125. package/dist/src/version.js +32 -0
  126. package/dist/src/version.js.map +7 -0
  127. package/package.json +73 -0
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # mcp-researchpowerpack
2
+
3
+ http mcp server for research. web search, reddit mining, scraping, deep research — all over `/mcp`.
4
+
5
+ built on [mcp-use](https://github.com/nicepkg/mcp-use). no stdio, http only.
6
+
7
+ ## tools
8
+
9
+ | tool | what it does | needs |
10
+ |------|-------------|-------|
11
+ | `web-search` | parallel google search across 3–100 keywords, ctr-weighted url ranking | `SERPER_API_KEY` |
12
+ | `search-reddit` | reddit-focused search, 3–50 diverse queries | `SERPER_API_KEY` |
13
+ | `get-reddit-post` | fetch reddit posts + full comment trees, 2–50 urls | `REDDIT_CLIENT_ID` + `REDDIT_CLIENT_SECRET` |
14
+ | `scrape-links` | scrape 1–50 urls with optional ai extraction | `SCRAPEDO_API_KEY` |
15
+ | `deep-research` | multi-question research with optional file attachments | `OPENROUTER_API_KEY` |
16
+
17
+ also exposes `/health` and `health://status` mcp resource.
18
+
19
+ ## quickstart
20
+
21
+ ```bash
22
+ # from npm
23
+ HOST=127.0.0.1 PORT=3000 npx -y mcp-researchpowerpack-http
24
+
25
+ # from source
26
+ git clone https://github.com/yigitkonur/mcp-researchpowerpack-http.git
27
+ cd mcp-researchpowerpack-http
28
+ pnpm install && pnpm dev
29
+ ```
30
+
31
+ connect your client to `http://localhost:3000/mcp`:
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "research-powerpack": {
37
+ "url": "http://localhost:3000/mcp"
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ ## config
44
+
45
+ copy `.env.example`, set only what you need. missing keys don't crash — they disable the tool with a clear error.
46
+
47
+ ### server
48
+
49
+ | var | default | |
50
+ |-----|---------|---|
51
+ | `PORT` | `3000` | http port |
52
+ | `HOST` | `127.0.0.1` | bind address |
53
+ | `ALLOWED_ORIGINS` | unset | comma-separated origins for host validation |
54
+ | `REDIS_URL` | unset | redis-backed sessions + distributed sse |
55
+
56
+ ### providers
57
+
58
+ | var | enables |
59
+ |-----|---------|
60
+ | `SERPER_API_KEY` | web-search, search-reddit |
61
+ | `REDDIT_CLIENT_ID` + `REDDIT_CLIENT_SECRET` | get-reddit-post |
62
+ | `SCRAPEDO_API_KEY` | scrape-links |
63
+ | `OPENROUTER_API_KEY` | deep-research, ai extraction |
64
+
65
+ optional tuning: `RESEARCH_MODEL`, `RESEARCH_FALLBACK_MODEL`, `LLM_EXTRACTION_MODEL`, `DEFAULT_REASONING_EFFORT`, `API_TIMEOUT_MS`.
66
+
67
+ ## dev
68
+
69
+ ```bash
70
+ pnpm install
71
+ pnpm dev # watch mode, serves :3000/mcp
72
+ pnpm typecheck # tsc --noEmit
73
+ pnpm test # http integration test
74
+ pnpm build # compile to dist/
75
+ pnpm inspect # mcp-use inspector
76
+ ```
77
+
78
+ ## deploy
79
+
80
+ ```bash
81
+ pnpm build
82
+ pnpm deploy # manufact cloud
83
+ ```
84
+
85
+ or self-host anywhere with node 20.19+ / 22.12+:
86
+
87
+ ```bash
88
+ HOST=0.0.0.0 ALLOWED_ORIGINS=https://app.example.com pnpm start
89
+ ```
90
+
91
+ ## architecture
92
+
93
+ ```
94
+ index.ts server startup, cors, health, shutdown
95
+ src/
96
+ config/ env parsing, capability detection, lazy proxy config
97
+ clients/ provider api clients (serper, reddit, scrapedo, openrouter)
98
+ tools/
99
+ registry.ts registerAllTools() — wires tools to mcp server
100
+ search.ts web-search handler
101
+ reddit.ts search-reddit + get-reddit-post
102
+ scrape.ts scrape-links handler
103
+ research.ts deep-research handler
104
+ mcp-helpers.ts response builders (markdown, error, toolFailure)
105
+ utils.ts shared formatters, token budget allocation
106
+ services/
107
+ llm-processor.ts ai extraction/synthesis via openrouter
108
+ file-attachment.ts local file reads for deep-research context
109
+ markdown-cleaner.ts html/markdown cleanup
110
+ schemas/ zod v4 input validation per tool
111
+ utils/
112
+ errors.ts structured error codes (retryable classification)
113
+ concurrency.ts pMap/pMapSettled — bounded parallel execution
114
+ retry.ts exponential backoff with jitter
115
+ url-aggregator.ts ctr-weighted url ranking for search consensus
116
+ response.ts formatSuccess/formatError/formatBatchHeader
117
+ logger.ts mcpLog() — stderr-only (mcp-safe)
118
+ ```
119
+
120
+ key patterns: capability detection at startup, lazy config via proxy, bounded concurrency (scraper:30, reddit:10, research:3), 32k token budgets, ctr-based url ranking, tools never throw (always return toolFailure), structured errors with retry classification.
121
+
122
+ ## license
123
+
124
+ mit
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env node
2
+ import { Logger } from "mcp-use";
3
+ import {
4
+ InMemorySessionStore,
5
+ InMemoryStreamManager,
6
+ MCPServer,
7
+ RedisSessionStore,
8
+ RedisStreamManager,
9
+ object
10
+ } from "mcp-use/server";
11
+ import { createClient } from "redis";
12
+ import { SERVER } from "./src/config/index.js";
13
+ import { registerAllTools } from "./src/tools/registry.js";
14
+ const DEFAULT_PORT = 3e3;
15
+ const SHUTDOWN_TIMEOUT_MS = 1e4;
16
+ const WEBSITE_URL = "https://github.com/yigitkonur/mcp-researchpowerpack-http";
17
+ const LOCAL_DEFAULT_HOST = "127.0.0.1";
18
+ const startupLogger = Logger.get("startup");
19
+ function parseCsvEnv(value) {
20
+ if (!value) return void 0;
21
+ const parts = value.split(",").map((part) => part.trim()).filter(Boolean);
22
+ return parts.length > 0 ? parts : void 0;
23
+ }
24
+ function parsePort(value, fallback) {
25
+ const parsed = Number.parseInt(value ?? "", 10);
26
+ if (Number.isFinite(parsed) && parsed > 0) {
27
+ return parsed;
28
+ }
29
+ return fallback;
30
+ }
31
+ function resolvePort() {
32
+ const portFlagIndex = process.argv.findIndex((arg) => arg === "--port");
33
+ if (portFlagIndex >= 0) {
34
+ return parsePort(process.argv[portFlagIndex + 1], DEFAULT_PORT);
35
+ }
36
+ return parsePort(process.env.PORT, DEFAULT_PORT);
37
+ }
38
+ function resolveHost() {
39
+ const explicitHost = process.env.HOST?.trim();
40
+ if (explicitHost) {
41
+ return explicitHost;
42
+ }
43
+ if (process.env.PORT?.trim()) {
44
+ return "0.0.0.0";
45
+ }
46
+ return LOCAL_DEFAULT_HOST;
47
+ }
48
+ function buildCors(allowedOrigins) {
49
+ if (!allowedOrigins || allowedOrigins.length === 0) {
50
+ return void 0;
51
+ }
52
+ return {
53
+ origin: allowedOrigins,
54
+ allowMethods: ["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"],
55
+ allowHeaders: [
56
+ "Content-Type",
57
+ "Accept",
58
+ "Authorization",
59
+ "mcp-protocol-version",
60
+ "mcp-session-id",
61
+ "X-Proxy-Token",
62
+ "X-Target-URL"
63
+ ],
64
+ exposeHeaders: ["mcp-session-id"]
65
+ };
66
+ }
67
+ function configureLogging() {
68
+ Logger.configure({
69
+ level: process.env.NODE_ENV === "production" ? "info" : "debug",
70
+ format: "minimal"
71
+ });
72
+ const debug = process.env.DEBUG?.trim();
73
+ if (debug === "2") {
74
+ Logger.setDebug(2);
75
+ } else if (debug) {
76
+ Logger.setDebug(1);
77
+ }
78
+ }
79
+ function normalizeOrigin(value, envName) {
80
+ try {
81
+ return new URL(value).origin;
82
+ } catch {
83
+ throw new Error(`${envName} must contain absolute URLs with protocol. Received: ${value}`);
84
+ }
85
+ }
86
+ function resolveAllowedOrigins() {
87
+ const explicitOrigins = parseCsvEnv(process.env.ALLOWED_ORIGINS);
88
+ if (explicitOrigins && explicitOrigins.length > 0) {
89
+ return explicitOrigins.map((origin) => normalizeOrigin(origin, "ALLOWED_ORIGINS"));
90
+ }
91
+ return void 0;
92
+ }
93
+ async function buildSessionConfig() {
94
+ const redisUrl = process.env.REDIS_URL?.trim();
95
+ if (!redisUrl) {
96
+ return {
97
+ sessionConfig: {
98
+ sessionStore: new InMemorySessionStore(),
99
+ streamManager: new InMemoryStreamManager()
100
+ },
101
+ cleanupFns: []
102
+ };
103
+ }
104
+ const commandClient = createClient({ url: redisUrl });
105
+ const pubSubClient = commandClient.duplicate();
106
+ await Promise.all([commandClient.connect(), pubSubClient.connect()]);
107
+ return {
108
+ sessionConfig: {
109
+ sessionStore: new RedisSessionStore({
110
+ client: commandClient
111
+ }),
112
+ streamManager: new RedisStreamManager({
113
+ client: commandClient,
114
+ pubSubClient
115
+ })
116
+ },
117
+ cleanupFns: [
118
+ async () => {
119
+ await pubSubClient.quit();
120
+ },
121
+ async () => {
122
+ await commandClient.quit();
123
+ }
124
+ ]
125
+ };
126
+ }
127
+ function buildHealthPayload(server, startedAt) {
128
+ return {
129
+ status: "ok",
130
+ name: SERVER.NAME,
131
+ version: SERVER.VERSION,
132
+ transport: "http",
133
+ uptime_seconds: Math.floor((Date.now() - startedAt) / 1e3),
134
+ active_sessions: server.getActiveSessions().length,
135
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
136
+ };
137
+ }
138
+ async function main() {
139
+ configureLogging();
140
+ const isProduction = process.env.NODE_ENV === "production";
141
+ const host = resolveHost();
142
+ const port = resolvePort();
143
+ const baseUrl = process.env.MCP_URL?.trim() || void 0;
144
+ const allowedOrigins = resolveAllowedOrigins();
145
+ const { sessionConfig, cleanupFns } = await buildSessionConfig();
146
+ startupLogger.info(`Starting ${SERVER.NAME} v${SERVER.VERSION}`);
147
+ startupLogger.info(`Binding HTTP server to ${host}:${port}`);
148
+ if (allowedOrigins && allowedOrigins.length > 0) {
149
+ startupLogger.info(`Host validation enabled for origins: ${allowedOrigins.join(", ")}`);
150
+ } else if (isProduction) {
151
+ startupLogger.warn(
152
+ "Host validation is disabled because ALLOWED_ORIGINS is not set. Set ALLOWED_ORIGINS to the public deployment URL or custom domain after the first deploy."
153
+ );
154
+ } else {
155
+ startupLogger.info("Host validation disabled for local development");
156
+ }
157
+ const server = new MCPServer({
158
+ name: SERVER.NAME,
159
+ title: "Research Powerpack",
160
+ version: SERVER.VERSION,
161
+ description: SERVER.DESCRIPTION,
162
+ websiteUrl: WEBSITE_URL,
163
+ host,
164
+ baseUrl,
165
+ cors: buildCors(allowedOrigins),
166
+ allowedOrigins,
167
+ ...sessionConfig
168
+ });
169
+ registerAllTools(server);
170
+ const startedAt = Date.now();
171
+ server.get("/health", (c) => c.json(buildHealthPayload(server, startedAt)));
172
+ server.get("/healthz", (c) => c.json(buildHealthPayload(server, startedAt)));
173
+ server.resource(
174
+ {
175
+ name: "server-health",
176
+ uri: "health://status",
177
+ description: "Current server health, uptime, and active MCP session count.",
178
+ mimeType: "application/json"
179
+ },
180
+ async () => object(buildHealthPayload(server, startedAt))
181
+ );
182
+ let isShuttingDown = false;
183
+ async function shutdown(signal, exitCode) {
184
+ if (isShuttingDown) return;
185
+ isShuttingDown = true;
186
+ const forceExit = setTimeout(() => {
187
+ startupLogger.error(`Forced exit after ${SHUTDOWN_TIMEOUT_MS}ms (${signal})`);
188
+ process.exit(1);
189
+ }, SHUTDOWN_TIMEOUT_MS);
190
+ try {
191
+ startupLogger.warn(`Shutdown signal received: ${signal}`);
192
+ await server.close();
193
+ for (const cleanupFn of cleanupFns) {
194
+ await cleanupFn();
195
+ }
196
+ clearTimeout(forceExit);
197
+ process.exit(exitCode);
198
+ } catch (error) {
199
+ clearTimeout(forceExit);
200
+ const message = error instanceof Error ? error.stack ?? error.message : String(error);
201
+ startupLogger.error(`Error while stopping server: ${message}`);
202
+ process.exit(1);
203
+ }
204
+ }
205
+ process.on("SIGTERM", () => {
206
+ void shutdown("SIGTERM", 0);
207
+ });
208
+ process.on("SIGINT", () => {
209
+ void shutdown("SIGINT", 0);
210
+ });
211
+ process.on("uncaughtException", (error) => {
212
+ startupLogger.error(`Uncaught exception: ${error.stack ?? error.message}`);
213
+ void shutdown("uncaughtException", 1);
214
+ });
215
+ process.on("unhandledRejection", (reason) => {
216
+ startupLogger.error(`Unhandled rejection: ${String(reason)}`);
217
+ void shutdown("unhandledRejection", 1);
218
+ });
219
+ await server.listen(port);
220
+ startupLogger.info(`${SERVER.NAME} v${SERVER.VERSION} listening on http://${host}:${port}/mcp`);
221
+ }
222
+ void main().catch((error) => {
223
+ const message = error instanceof Error ? error.stack ?? error.message : String(error);
224
+ startupLogger.error(`Server failed to start: ${message}`);
225
+ process.exit(1);
226
+ });
227
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../index.ts"],
4
+ "sourcesContent": ["#!/usr/bin/env node\n\nimport { Logger } from 'mcp-use';\nimport {\n InMemorySessionStore,\n InMemoryStreamManager,\n MCPServer,\n RedisSessionStore,\n RedisStreamManager,\n object,\n type ServerConfig,\n} from 'mcp-use/server';\nimport { createClient, type RedisClientType } from 'redis';\n\nimport { SERVER } from './src/config/index.js';\nimport { registerAllTools } from './src/tools/registry.js';\n\nconst DEFAULT_PORT = 3000 as const;\nconst SHUTDOWN_TIMEOUT_MS = 10_000 as const;\nconst WEBSITE_URL = 'https://github.com/yigitkonur/mcp-researchpowerpack-http' as const;\nconst LOCAL_DEFAULT_HOST = '127.0.0.1' as const;\n\ntype CleanupFn = () => Promise<void>;\n\nconst startupLogger = Logger.get('startup');\n\nfunction parseCsvEnv(value: string | undefined): string[] | undefined {\n if (!value) return undefined;\n\n const parts = value\n .split(',')\n .map((part) => part.trim())\n .filter(Boolean);\n\n return parts.length > 0 ? parts : undefined;\n}\n\nfunction parsePort(value: string | undefined, fallback: number): number {\n const parsed = Number.parseInt(value ?? '', 10);\n if (Number.isFinite(parsed) && parsed > 0) {\n return parsed;\n }\n\n return fallback;\n}\n\nfunction resolvePort(): number {\n const portFlagIndex = process.argv.findIndex((arg) => arg === '--port');\n if (portFlagIndex >= 0) {\n return parsePort(process.argv[portFlagIndex + 1], DEFAULT_PORT);\n }\n\n return parsePort(process.env.PORT, DEFAULT_PORT);\n}\n\nfunction resolveHost(): string {\n const explicitHost = process.env.HOST?.trim();\n if (explicitHost) {\n return explicitHost;\n }\n\n // Cloud runtimes typically inject PORT and expect the process to listen on all interfaces.\n if (process.env.PORT?.trim()) {\n return '0.0.0.0';\n }\n\n return LOCAL_DEFAULT_HOST;\n}\n\nfunction buildCors(allowedOrigins: string[] | undefined): ServerConfig['cors'] {\n if (!allowedOrigins || allowedOrigins.length === 0) {\n return undefined;\n }\n\n return {\n origin: allowedOrigins,\n allowMethods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS'],\n allowHeaders: [\n 'Content-Type',\n 'Accept',\n 'Authorization',\n 'mcp-protocol-version',\n 'mcp-session-id',\n 'X-Proxy-Token',\n 'X-Target-URL',\n ],\n exposeHeaders: ['mcp-session-id'],\n };\n}\n\nfunction configureLogging(): void {\n Logger.configure({\n level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',\n format: 'minimal',\n });\n\n const debug = process.env.DEBUG?.trim();\n if (debug === '2') {\n Logger.setDebug(2);\n } else if (debug) {\n Logger.setDebug(1);\n }\n}\n\nfunction normalizeOrigin(value: string, envName: string): string {\n try {\n return new URL(value).origin;\n } catch {\n throw new Error(`${envName} must contain absolute URLs with protocol. Received: ${value}`);\n }\n}\n\nfunction resolveAllowedOrigins(): string[] | undefined {\n const explicitOrigins = parseCsvEnv(process.env.ALLOWED_ORIGINS);\n if (explicitOrigins && explicitOrigins.length > 0) {\n return explicitOrigins.map(origin => normalizeOrigin(origin, 'ALLOWED_ORIGINS'));\n }\n\n return undefined;\n}\n\nasync function buildSessionConfig(): Promise<{\n sessionConfig: Pick<ServerConfig, 'sessionStore' | 'streamManager'>;\n cleanupFns: CleanupFn[];\n}> {\n const redisUrl = process.env.REDIS_URL?.trim();\n\n if (!redisUrl) {\n return {\n sessionConfig: {\n sessionStore: new InMemorySessionStore(),\n streamManager: new InMemoryStreamManager(),\n },\n cleanupFns: [],\n };\n }\n\n const commandClient = createClient({ url: redisUrl });\n const pubSubClient = commandClient.duplicate();\n\n await Promise.all([commandClient.connect(), pubSubClient.connect()]);\n\n return {\n sessionConfig: {\n sessionStore: new RedisSessionStore({\n client: commandClient as RedisClientType,\n }),\n streamManager: new RedisStreamManager({\n client: commandClient as RedisClientType,\n pubSubClient: pubSubClient as RedisClientType,\n }),\n },\n cleanupFns: [\n async () => {\n await pubSubClient.quit();\n },\n async () => {\n await commandClient.quit();\n },\n ],\n };\n}\n\nfunction buildHealthPayload(server: MCPServer, startedAt: number) {\n return {\n status: 'ok',\n name: SERVER.NAME,\n version: SERVER.VERSION,\n transport: 'http',\n uptime_seconds: Math.floor((Date.now() - startedAt) / 1000),\n active_sessions: server.getActiveSessions().length,\n timestamp: new Date().toISOString(),\n };\n}\n\nasync function main(): Promise<void> {\n configureLogging();\n\n const isProduction = process.env.NODE_ENV === 'production';\n const host = resolveHost();\n const port = resolvePort();\n const baseUrl = process.env.MCP_URL?.trim() || undefined;\n const allowedOrigins = resolveAllowedOrigins();\n\n const { sessionConfig, cleanupFns } = await buildSessionConfig();\n\n startupLogger.info(`Starting ${SERVER.NAME} v${SERVER.VERSION}`);\n startupLogger.info(`Binding HTTP server to ${host}:${port}`);\n if (allowedOrigins && allowedOrigins.length > 0) {\n startupLogger.info(`Host validation enabled for origins: ${allowedOrigins.join(', ')}`);\n } else if (isProduction) {\n startupLogger.warn(\n 'Host validation is disabled because ALLOWED_ORIGINS is not set. ' +\n 'Set ALLOWED_ORIGINS to the public deployment URL or custom domain after the first deploy.',\n );\n } else {\n startupLogger.info('Host validation disabled for local development');\n }\n\n const server = new MCPServer({\n name: SERVER.NAME,\n title: 'Research Powerpack',\n version: SERVER.VERSION,\n description: SERVER.DESCRIPTION,\n websiteUrl: WEBSITE_URL,\n host,\n baseUrl,\n cors: buildCors(allowedOrigins),\n allowedOrigins,\n ...sessionConfig,\n });\n\n registerAllTools(server);\n\n const startedAt = Date.now();\n\n server.get('/health', (c) => c.json(buildHealthPayload(server, startedAt)));\n server.get('/healthz', (c) => c.json(buildHealthPayload(server, startedAt)));\n server.resource(\n {\n name: 'server-health',\n uri: 'health://status',\n description: 'Current server health, uptime, and active MCP session count.',\n mimeType: 'application/json',\n },\n async () => object(buildHealthPayload(server, startedAt)),\n );\n\n let isShuttingDown = false;\n\n async function shutdown(signal: string, exitCode: number): Promise<void> {\n if (isShuttingDown) return;\n isShuttingDown = true;\n\n const forceExit = setTimeout(() => {\n startupLogger.error(`Forced exit after ${SHUTDOWN_TIMEOUT_MS}ms (${signal})`);\n process.exit(1);\n }, SHUTDOWN_TIMEOUT_MS);\n\n try {\n startupLogger.warn(`Shutdown signal received: ${signal}`);\n await server.close();\n\n for (const cleanupFn of cleanupFns) {\n await cleanupFn();\n }\n\n clearTimeout(forceExit);\n process.exit(exitCode);\n } catch (error) {\n clearTimeout(forceExit);\n const message = error instanceof Error ? (error.stack ?? error.message) : String(error);\n startupLogger.error(`Error while stopping server: ${message}`);\n process.exit(1);\n }\n }\n\n process.on('SIGTERM', () => {\n void shutdown('SIGTERM', 0);\n });\n\n process.on('SIGINT', () => {\n void shutdown('SIGINT', 0);\n });\n\n process.on('uncaughtException', (error) => {\n startupLogger.error(`Uncaught exception: ${error.stack ?? error.message}`);\n void shutdown('uncaughtException', 1);\n });\n\n process.on('unhandledRejection', (reason) => {\n startupLogger.error(`Unhandled rejection: ${String(reason)}`);\n void shutdown('unhandledRejection', 1);\n });\n\n await server.listen(port);\n\n startupLogger.info(`${SERVER.NAME} v${SERVER.VERSION} listening on http://${host}:${port}/mcp`);\n}\n\nvoid main().catch((error) => {\n const message = error instanceof Error ? (error.stack ?? error.message) : String(error);\n startupLogger.error(`Server failed to start: ${message}`);\n process.exit(1);\n});\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "includeInspector": false,
3
+ "buildTime": "2026-03-26T14:07:11.298Z",
4
+ "buildId": "d76749f7bf0957bb",
5
+ "entryPoint": "dist/index.js",
6
+ "widgets": {}
7
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * GitHub API Client
3
+ * GraphQL + REST client for fetching repository data for quality scoring.
4
+ * Implements robust error handling that NEVER crashes.
5
+ */
6
+ import { type StructuredError } from '../utils/errors.js';
7
+ export interface GitHubSearchItem {
8
+ readonly fullName: string;
9
+ readonly owner: string;
10
+ readonly name: string;
11
+ readonly description: string | null;
12
+ readonly stars: number;
13
+ readonly forks: number;
14
+ readonly language: string | null;
15
+ readonly pushedAt: string;
16
+ readonly url: string;
17
+ }
18
+ export interface ParticipationData {
19
+ readonly all: readonly number[];
20
+ readonly owner: readonly number[];
21
+ }
22
+ export interface ContributorEntry {
23
+ readonly login: string;
24
+ readonly contributions: number;
25
+ }
26
+ export interface RepoGraphQLData {
27
+ readonly stars: number;
28
+ readonly forks: number;
29
+ readonly watchers: number;
30
+ readonly openIssues: number;
31
+ readonly closedIssues: number;
32
+ readonly totalCommits: number;
33
+ readonly totalReleases: number;
34
+ readonly totalPRs: number;
35
+ readonly sizeKb: number;
36
+ readonly language: string | null;
37
+ readonly license: string | null;
38
+ readonly archived: boolean;
39
+ readonly createdAt: string;
40
+ readonly pushedAt: string;
41
+ readonly description: string | null;
42
+ readonly homepage: string | null;
43
+ readonly hasCI: boolean;
44
+ readonly hasContributing: boolean;
45
+ readonly hasIssueTemplate: boolean;
46
+ readonly hasPrTemplate: boolean;
47
+ readonly hasCodeOfConduct: boolean;
48
+ readonly hasTopics: boolean;
49
+ }
50
+ export interface RepoFullData {
51
+ readonly graphql: RepoGraphQLData;
52
+ readonly participation: ParticipationData;
53
+ readonly contributors: readonly ContributorEntry[];
54
+ }
55
+ export declare class GitHubClient {
56
+ private token;
57
+ private apiCallCount;
58
+ constructor(token?: string);
59
+ get apiCalls(): number;
60
+ searchRepos(query: string, sort?: string, perPage?: number): Promise<{
61
+ items: GitHubSearchItem[];
62
+ totalCount: number;
63
+ error?: StructuredError;
64
+ }>;
65
+ fetchRepoDetails(owner: string, name: string): Promise<{
66
+ data?: RepoFullData;
67
+ error?: StructuredError;
68
+ }>;
69
+ fetchMultipleRepoDetails(repos: Array<{
70
+ owner: string;
71
+ name: string;
72
+ }>): Promise<Array<{
73
+ owner: string;
74
+ name: string;
75
+ data?: RepoFullData;
76
+ error?: StructuredError;
77
+ }>>;
78
+ private graphqlQuery;
79
+ private fetchParticipation;
80
+ private fetchContributors;
81
+ private restGet;
82
+ }
83
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../../src/clients/github.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,oBAAoB,CAAC;AAS5B,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,YAAY,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACpD;AAkFD,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAK;gBAEb,KAAK,CAAC,EAAE,MAAM;IAW1B,IAAI,QAAQ,IAAI,MAAM,CAErB;IAMK,WAAW,CACf,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,MAAgB,EACtB,OAAO,GAAE,MAAW,GACnB,OAAO,CAAC;QAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,eAAe,CAAA;KAAE,CAAC;IAsChF,gBAAgB,CACpB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,YAAY,CAAC;QAAC,KAAK,CAAC,EAAE,eAAe,CAAA;KAAE,CAAC;IAiCtD,wBAAwB,CAC5B,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAC5C,OAAO,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,YAAY,CAAC;QAAC,KAAK,CAAC,EAAE,eAAe,CAAA;KAAE,CAAC,CAAC;YA2BlF,YAAY;YAgEZ,kBAAkB;YAmDlB,iBAAiB;YA2CjB,OAAO;CAwCtB"}