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.
- package/README.md +124 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +227 -0
- package/dist/index.js.map +7 -0
- package/dist/mcp-use.json +7 -0
- package/dist/src/clients/github.d.ts +83 -0
- package/dist/src/clients/github.d.ts.map +1 -0
- package/dist/src/clients/github.js +370 -0
- package/dist/src/clients/github.js.map +7 -0
- package/dist/src/clients/reddit.d.ts +60 -0
- package/dist/src/clients/reddit.d.ts.map +1 -0
- package/dist/src/clients/reddit.js +287 -0
- package/dist/src/clients/reddit.js.map +7 -0
- package/dist/src/clients/research.d.ts +67 -0
- package/dist/src/clients/research.d.ts.map +1 -0
- package/dist/src/clients/research.js +282 -0
- package/dist/src/clients/research.js.map +7 -0
- package/dist/src/clients/scraper.d.ts +72 -0
- package/dist/src/clients/scraper.d.ts.map +1 -0
- package/dist/src/clients/scraper.js +327 -0
- package/dist/src/clients/scraper.js.map +7 -0
- package/dist/src/clients/search.d.ts +57 -0
- package/dist/src/clients/search.d.ts.map +1 -0
- package/dist/src/clients/search.js +218 -0
- package/dist/src/clients/search.js.map +7 -0
- package/dist/src/config/index.d.ts +93 -0
- package/dist/src/config/index.d.ts.map +1 -0
- package/dist/src/config/index.js +218 -0
- package/dist/src/config/index.js.map +7 -0
- package/dist/src/schemas/deep-research.d.ts +40 -0
- package/dist/src/schemas/deep-research.d.ts.map +1 -0
- package/dist/src/schemas/deep-research.js +216 -0
- package/dist/src/schemas/deep-research.js.map +7 -0
- package/dist/src/schemas/github-score.d.ts +50 -0
- package/dist/src/schemas/github-score.d.ts.map +1 -0
- package/dist/src/schemas/github-score.js +58 -0
- package/dist/src/schemas/github-score.js.map +7 -0
- package/dist/src/schemas/scrape-links.d.ts +23 -0
- package/dist/src/schemas/scrape-links.d.ts.map +1 -0
- package/dist/src/schemas/scrape-links.js +32 -0
- package/dist/src/schemas/scrape-links.js.map +7 -0
- package/dist/src/schemas/web-search.d.ts +18 -0
- package/dist/src/schemas/web-search.d.ts.map +1 -0
- package/dist/src/schemas/web-search.js +28 -0
- package/dist/src/schemas/web-search.js.map +7 -0
- package/dist/src/scoring/github-quality.d.ts +142 -0
- package/dist/src/scoring/github-quality.d.ts.map +1 -0
- package/dist/src/scoring/github-quality.js +202 -0
- package/dist/src/scoring/github-quality.js.map +7 -0
- package/dist/src/services/file-attachment.d.ts +30 -0
- package/dist/src/services/file-attachment.d.ts.map +1 -0
- package/dist/src/services/file-attachment.js +205 -0
- package/dist/src/services/file-attachment.js.map +7 -0
- package/dist/src/services/llm-processor.d.ts +29 -0
- package/dist/src/services/llm-processor.d.ts.map +1 -0
- package/dist/src/services/llm-processor.js +206 -0
- package/dist/src/services/llm-processor.js.map +7 -0
- package/dist/src/services/markdown-cleaner.d.ts +8 -0
- package/dist/src/services/markdown-cleaner.d.ts.map +1 -0
- package/dist/src/services/markdown-cleaner.js +63 -0
- package/dist/src/services/markdown-cleaner.js.map +7 -0
- package/dist/src/tools/github-score.d.ts +12 -0
- package/dist/src/tools/github-score.d.ts.map +1 -0
- package/dist/src/tools/github-score.js +306 -0
- package/dist/src/tools/github-score.js.map +7 -0
- package/dist/src/tools/mcp-helpers.d.ts +27 -0
- package/dist/src/tools/mcp-helpers.d.ts.map +1 -0
- package/dist/src/tools/mcp-helpers.js +47 -0
- package/dist/src/tools/mcp-helpers.js.map +7 -0
- package/dist/src/tools/reddit.d.ts +54 -0
- package/dist/src/tools/reddit.d.ts.map +1 -0
- package/dist/src/tools/reddit.js +498 -0
- package/dist/src/tools/reddit.js.map +7 -0
- package/dist/src/tools/registry.d.ts +3 -0
- package/dist/src/tools/registry.d.ts.map +1 -0
- package/dist/src/tools/registry.js +17 -0
- package/dist/src/tools/registry.js.map +7 -0
- package/dist/src/tools/research.d.ts +14 -0
- package/dist/src/tools/research.d.ts.map +1 -0
- package/dist/src/tools/research.js +250 -0
- package/dist/src/tools/research.js.map +7 -0
- package/dist/src/tools/scrape.d.ts +14 -0
- package/dist/src/tools/scrape.d.ts.map +1 -0
- package/dist/src/tools/scrape.js +290 -0
- package/dist/src/tools/scrape.js.map +7 -0
- package/dist/src/tools/search.d.ts +10 -0
- package/dist/src/tools/search.d.ts.map +1 -0
- package/dist/src/tools/search.js +197 -0
- package/dist/src/tools/search.js.map +7 -0
- package/dist/src/tools/utils.d.ts +105 -0
- package/dist/src/tools/utils.d.ts.map +1 -0
- package/dist/src/tools/utils.js +96 -0
- package/dist/src/tools/utils.js.map +7 -0
- package/dist/src/utils/concurrency.d.ts +28 -0
- package/dist/src/utils/concurrency.d.ts.map +1 -0
- package/dist/src/utils/concurrency.js +62 -0
- package/dist/src/utils/concurrency.js.map +7 -0
- package/dist/src/utils/errors.d.ts +95 -0
- package/dist/src/utils/errors.d.ts.map +1 -0
- package/dist/src/utils/errors.js +289 -0
- package/dist/src/utils/errors.js.map +7 -0
- package/dist/src/utils/logger.d.ts +33 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +41 -0
- package/dist/src/utils/logger.js.map +7 -0
- package/dist/src/utils/markdown-formatter.d.ts +5 -0
- package/dist/src/utils/markdown-formatter.d.ts.map +1 -0
- package/dist/src/utils/markdown-formatter.js +15 -0
- package/dist/src/utils/markdown-formatter.js.map +7 -0
- package/dist/src/utils/response.d.ts +83 -0
- package/dist/src/utils/response.d.ts.map +1 -0
- package/dist/src/utils/response.js +109 -0
- package/dist/src/utils/response.js.map +7 -0
- package/dist/src/utils/retry.d.ts +43 -0
- package/dist/src/utils/retry.d.ts.map +1 -0
- package/dist/src/utils/retry.js +37 -0
- package/dist/src/utils/retry.js.map +7 -0
- package/dist/src/utils/url-aggregator.d.ts +92 -0
- package/dist/src/utils/url-aggregator.d.ts.map +1 -0
- package/dist/src/utils/url-aggregator.js +357 -0
- package/dist/src/utils/url-aggregator.js.map +7 -0
- package/dist/src/version.d.ts +28 -0
- package/dist/src/version.d.ts.map +1 -0
- package/dist/src/version.js +32 -0
- package/dist/src/version.js.map +7 -0
- package/package.json +73 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { getCapabilities, getMissingEnvMessage } from "../config/index.js";
|
|
2
|
+
import {
|
|
3
|
+
githubScoreOutputSchema,
|
|
4
|
+
githubScoreParamsSchema
|
|
5
|
+
} from "../schemas/github-score.js";
|
|
6
|
+
import { GitHubClient } from "../clients/github.js";
|
|
7
|
+
import {
|
|
8
|
+
scoreRepo
|
|
9
|
+
} from "../scoring/github-quality.js";
|
|
10
|
+
import { classifyError } from "../utils/errors.js";
|
|
11
|
+
import { mcpLog, formatError, formatDuration } from "./utils.js";
|
|
12
|
+
import {
|
|
13
|
+
createToolReporter,
|
|
14
|
+
NOOP_REPORTER,
|
|
15
|
+
toolFailure,
|
|
16
|
+
toolSuccess,
|
|
17
|
+
toToolResponse
|
|
18
|
+
} from "./mcp-helpers.js";
|
|
19
|
+
function buildSearchQuery(params) {
|
|
20
|
+
let query;
|
|
21
|
+
if (params.search_mode === "OR") {
|
|
22
|
+
query = params.keywords.join(" OR ");
|
|
23
|
+
} else {
|
|
24
|
+
query = params.keywords.join(" ");
|
|
25
|
+
}
|
|
26
|
+
if (params.min_stars > 0) {
|
|
27
|
+
query += ` stars:>=${params.min_stars}`;
|
|
28
|
+
}
|
|
29
|
+
if (params.language) {
|
|
30
|
+
query += ` language:${params.language}`;
|
|
31
|
+
}
|
|
32
|
+
return query;
|
|
33
|
+
}
|
|
34
|
+
function buildRawRepoData(fullData) {
|
|
35
|
+
const g = fullData.graphql;
|
|
36
|
+
const disciplineFlags = {
|
|
37
|
+
hasLicense: g.license !== null,
|
|
38
|
+
hasContributing: g.hasContributing,
|
|
39
|
+
hasIssueTemplate: g.hasIssueTemplate,
|
|
40
|
+
hasPrTemplate: g.hasPrTemplate,
|
|
41
|
+
hasCodeOfConduct: g.hasCodeOfConduct,
|
|
42
|
+
hasCI: g.hasCI,
|
|
43
|
+
hasReleases: g.totalReleases > 0,
|
|
44
|
+
hasTopics: g.hasTopics,
|
|
45
|
+
hasDescription: (g.description ?? "").length > 0,
|
|
46
|
+
hasHomepage: (g.homepage ?? "").length > 0
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
stars: g.stars,
|
|
50
|
+
forks: g.forks,
|
|
51
|
+
watchers: g.watchers,
|
|
52
|
+
sizeKb: g.sizeKb,
|
|
53
|
+
createdAt: new Date(g.createdAt),
|
|
54
|
+
archived: g.archived,
|
|
55
|
+
hasLicense: g.license !== null,
|
|
56
|
+
closedIssues: g.closedIssues,
|
|
57
|
+
totalIssues: g.openIssues + g.closedIssues,
|
|
58
|
+
totalCommits: g.totalCommits,
|
|
59
|
+
contributorCommits: fullData.contributors.map((c) => c.contributions),
|
|
60
|
+
allWeeklyCommits: fullData.participation.all,
|
|
61
|
+
ownerWeeklyCommits: fullData.participation.owner,
|
|
62
|
+
disciplineFlags
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function formatRelativeDate(isoDate) {
|
|
66
|
+
const date = new Date(isoDate);
|
|
67
|
+
if (isNaN(date.getTime())) return "unknown";
|
|
68
|
+
const diffMs = Date.now() - date.getTime();
|
|
69
|
+
const days = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
70
|
+
if (days < 1) return "today";
|
|
71
|
+
if (days === 1) return "1d ago";
|
|
72
|
+
if (days < 30) return `${days}d ago`;
|
|
73
|
+
if (days < 365) return `${Math.floor(days / 30)}mo ago`;
|
|
74
|
+
return `${(days / 365).toFixed(1)}y ago`;
|
|
75
|
+
}
|
|
76
|
+
function formatStarCount(stars) {
|
|
77
|
+
if (stars >= 1e3) {
|
|
78
|
+
return `${(stars / 1e3).toFixed(1)}k`;
|
|
79
|
+
}
|
|
80
|
+
return String(stars);
|
|
81
|
+
}
|
|
82
|
+
function scoreBand(score) {
|
|
83
|
+
if (score >= 70) return "Excellent";
|
|
84
|
+
if (score >= 50) return "Good";
|
|
85
|
+
if (score >= 30) return "Meh";
|
|
86
|
+
return "Low";
|
|
87
|
+
}
|
|
88
|
+
function formatScoreTable(repos) {
|
|
89
|
+
let md = `| # | Repo | Stars | Score | Maint | Community | Discipline | Substance | Last Commit | Contributors | Flags |
|
|
90
|
+
`;
|
|
91
|
+
md += `|---|------|-------|-------|-------|-----------|------------|-----------|-------------|--------------|-------|
|
|
92
|
+
`;
|
|
93
|
+
repos.forEach((repo, i) => {
|
|
94
|
+
const s = repo.score;
|
|
95
|
+
const flagStr = s.flags.length > 0 ? s.flags.join(", ") : "-";
|
|
96
|
+
md += `| ${i + 1} | [${repo.fullName}](${repo.url}) | ${formatStarCount(repo.stars)} | **${s.score}** | ${s.subScores.maintenance.toFixed(2)} | ${s.subScores.community.toFixed(2)} | ${s.subScores.discipline.toFixed(2)} | ${s.subScores.substance.toFixed(2)} | ${formatRelativeDate(repo.lastPush)} | ${repo.contributors} | ${flagStr} |
|
|
97
|
+
`;
|
|
98
|
+
});
|
|
99
|
+
return md;
|
|
100
|
+
}
|
|
101
|
+
function sortRepos(repos, sortBy) {
|
|
102
|
+
const sorted = [...repos];
|
|
103
|
+
switch (sortBy) {
|
|
104
|
+
case "stars":
|
|
105
|
+
sorted.sort((a, b) => b.stars - a.stars);
|
|
106
|
+
break;
|
|
107
|
+
case "updated":
|
|
108
|
+
sorted.sort(
|
|
109
|
+
(a, b) => new Date(b.lastPush).getTime() - new Date(a.lastPush).getTime()
|
|
110
|
+
);
|
|
111
|
+
break;
|
|
112
|
+
case "score":
|
|
113
|
+
default:
|
|
114
|
+
sorted.sort((a, b) => b.score.score - a.score.score);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
return sorted;
|
|
118
|
+
}
|
|
119
|
+
async function handleGitHubScore(params, reporter = NOOP_REPORTER) {
|
|
120
|
+
const startTime = Date.now();
|
|
121
|
+
try {
|
|
122
|
+
const client = new GitHubClient();
|
|
123
|
+
const query = buildSearchQuery(params);
|
|
124
|
+
mcpLog("info", `Searching GitHub: "${query}"`, "github-score");
|
|
125
|
+
await reporter.log("info", `Searching GitHub for: ${query}`);
|
|
126
|
+
await reporter.progress(10, 100, "Searching GitHub repositories");
|
|
127
|
+
const searchResult = await client.searchRepos(query, "stars", params.max_results);
|
|
128
|
+
if (searchResult.error) {
|
|
129
|
+
return buildGitHubScoreError(searchResult.error, query, startTime, client.apiCalls);
|
|
130
|
+
}
|
|
131
|
+
if (searchResult.items.length === 0) {
|
|
132
|
+
const emptyMd = `# GitHub Score: No Results
|
|
133
|
+
|
|
134
|
+
Query: \`${query}\`
|
|
135
|
+
|
|
136
|
+
No repositories found. Try broader keywords or lower the min_stars filter.`;
|
|
137
|
+
return toolSuccess(emptyMd, {
|
|
138
|
+
content: emptyMd,
|
|
139
|
+
metadata: {
|
|
140
|
+
query,
|
|
141
|
+
total_found: 0,
|
|
142
|
+
total_scored: 0,
|
|
143
|
+
failed_scores: 0,
|
|
144
|
+
execution_time_ms: Date.now() - startTime,
|
|
145
|
+
api_calls_made: client.apiCalls
|
|
146
|
+
},
|
|
147
|
+
repos: []
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
mcpLog("info", `Found ${searchResult.totalCount} repos, scoring top ${searchResult.items.length}`, "github-score");
|
|
151
|
+
await reporter.progress(20, 100, `Found ${searchResult.items.length} repos, fetching details`);
|
|
152
|
+
const repoRequests = searchResult.items.map((item) => ({
|
|
153
|
+
owner: item.owner,
|
|
154
|
+
name: item.name
|
|
155
|
+
}));
|
|
156
|
+
const detailResults = await client.fetchMultipleRepoDetails(repoRequests);
|
|
157
|
+
const scoredRepos = [];
|
|
158
|
+
let failedCount = 0;
|
|
159
|
+
for (let i = 0; i < detailResults.length; i++) {
|
|
160
|
+
const detail = detailResults[i];
|
|
161
|
+
const searchItem = searchResult.items[i];
|
|
162
|
+
if (detail.error || !detail.data) {
|
|
163
|
+
failedCount++;
|
|
164
|
+
mcpLog("warning", `Failed to score ${searchItem.fullName}: ${detail.error?.message ?? "unknown"}`, "github-score");
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const rawData = buildRawRepoData(detail.data);
|
|
169
|
+
const score = scoreRepo(rawData);
|
|
170
|
+
scoredRepos.push({
|
|
171
|
+
fullName: searchItem.fullName,
|
|
172
|
+
url: searchItem.url,
|
|
173
|
+
stars: detail.data.graphql.stars,
|
|
174
|
+
forks: detail.data.graphql.forks,
|
|
175
|
+
openIssues: detail.data.graphql.openIssues,
|
|
176
|
+
language: detail.data.graphql.language,
|
|
177
|
+
lastPush: detail.data.graphql.pushedAt,
|
|
178
|
+
contributors: detail.data.contributors.length,
|
|
179
|
+
archived: detail.data.graphql.archived,
|
|
180
|
+
score
|
|
181
|
+
});
|
|
182
|
+
} catch {
|
|
183
|
+
failedCount++;
|
|
184
|
+
mcpLog("warning", `Scoring error for ${searchItem.fullName}`, "github-score");
|
|
185
|
+
}
|
|
186
|
+
const progress = 20 + Math.round((i + 1) / detailResults.length * 70);
|
|
187
|
+
await reporter.progress(progress, 100, `Scored ${i + 1}/${detailResults.length} repos`);
|
|
188
|
+
}
|
|
189
|
+
const sorted = sortRepos(scoredRepos, params.sort);
|
|
190
|
+
const executionTime = Date.now() - startTime;
|
|
191
|
+
const avgScore = sorted.length > 0 ? Math.round(sorted.reduce((s, r) => s + r.score.score, 0) / sorted.length) : 0;
|
|
192
|
+
let md = `# GitHub Score Report
|
|
193
|
+
|
|
194
|
+
`;
|
|
195
|
+
md += `**Query:** \`${query}\` | **Sorted by:** ${params.sort} | **Scored:** ${sorted.length}/${searchResult.items.length}`;
|
|
196
|
+
if (failedCount > 0) md += ` | **Failed:** ${failedCount}`;
|
|
197
|
+
md += `
|
|
198
|
+
|
|
199
|
+
`;
|
|
200
|
+
md += `**Average Score:** ${avgScore}/100 (${scoreBand(avgScore)})
|
|
201
|
+
|
|
202
|
+
`;
|
|
203
|
+
md += formatScoreTable(sorted);
|
|
204
|
+
md += `
|
|
205
|
+
---
|
|
206
|
+
`;
|
|
207
|
+
md += `*${formatDuration(executionTime)} | ${client.apiCalls} API calls | Score legend: Maint=Maintenance Pulse, Community=Community Health, Discipline=Engineering Practices, Substance=Code Quality*
|
|
208
|
+
`;
|
|
209
|
+
md += `
|
|
210
|
+
**Score bands:** 70+ Excellent | 50-69 Good | 30-49 Meh | <30 Low
|
|
211
|
+
`;
|
|
212
|
+
md += `
|
|
213
|
+
**Flag legend:** consistent-commits (steady work), growing (accelerating), active-community (external contributors), well-organized (CI/license/templates), high-bus-factor (multiple key contributors), stale-6mo (owner inactive), single-maintainer, no-license, archived, ai-dump-signal (few commits + large codebase)`;
|
|
214
|
+
const structuredRepos = sorted.map((r) => ({
|
|
215
|
+
full_name: r.fullName,
|
|
216
|
+
url: r.url,
|
|
217
|
+
stars: r.stars,
|
|
218
|
+
forks: r.forks,
|
|
219
|
+
open_issues: r.openIssues,
|
|
220
|
+
language: r.language,
|
|
221
|
+
last_push: r.lastPush,
|
|
222
|
+
contributors: r.contributors,
|
|
223
|
+
archived: r.archived,
|
|
224
|
+
composite_score: r.score.score,
|
|
225
|
+
maintenance_score: r.score.subScores.maintenance,
|
|
226
|
+
community_score: r.score.subScores.community,
|
|
227
|
+
discipline_score: r.score.subScores.discipline,
|
|
228
|
+
substance_score: r.score.subScores.substance,
|
|
229
|
+
flags: r.score.flags
|
|
230
|
+
}));
|
|
231
|
+
return toolSuccess(md, {
|
|
232
|
+
content: md,
|
|
233
|
+
metadata: {
|
|
234
|
+
query,
|
|
235
|
+
total_found: searchResult.totalCount,
|
|
236
|
+
total_scored: sorted.length,
|
|
237
|
+
failed_scores: failedCount,
|
|
238
|
+
execution_time_ms: executionTime,
|
|
239
|
+
api_calls_made: client.apiCalls
|
|
240
|
+
},
|
|
241
|
+
repos: structuredRepos
|
|
242
|
+
});
|
|
243
|
+
} catch (error) {
|
|
244
|
+
return buildGitHubScoreError(classifyError(error), buildSearchQuery(params), startTime, 0);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function buildGitHubScoreError(error, query, startTime, apiCalls) {
|
|
248
|
+
const structuredError = typeof error === "object" && error !== null && "code" in error ? error : classifyError(error);
|
|
249
|
+
mcpLog("error", `github-score: ${structuredError.message}`, "github-score");
|
|
250
|
+
const errorContent = formatError({
|
|
251
|
+
code: structuredError.code,
|
|
252
|
+
message: structuredError.message,
|
|
253
|
+
retryable: structuredError.retryable,
|
|
254
|
+
toolName: "github-score",
|
|
255
|
+
howToFix: [
|
|
256
|
+
"Verify GITHUB_TOKEN is set correctly",
|
|
257
|
+
"Check that the token has not expired",
|
|
258
|
+
"Ensure you have not exceeded the GitHub API rate limit (5000 req/hr)"
|
|
259
|
+
],
|
|
260
|
+
alternatives: [
|
|
261
|
+
'web-search(keywords=["topic github"]) \u2014 search the web for GitHub repos instead'
|
|
262
|
+
]
|
|
263
|
+
});
|
|
264
|
+
return toolFailure(
|
|
265
|
+
`${errorContent}
|
|
266
|
+
|
|
267
|
+
Query: ${query}
|
|
268
|
+
Execution time: ${formatDuration(Date.now() - startTime)}
|
|
269
|
+
API calls: ${apiCalls}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
function registerGitHubScoreTool(server) {
|
|
273
|
+
server.tool(
|
|
274
|
+
{
|
|
275
|
+
name: "github-score",
|
|
276
|
+
title: "GitHub Score",
|
|
277
|
+
description: 'Search GitHub repositories and score them with a composite "Gives a Damn" quality metric. Takes keywords with AND/OR logic, fetches repo metadata, commit patterns, contributor data, and CI/release/template presence to compute a 0-100 quality score across 4 dimensions: Maintenance (commit consistency, velocity, owner activity), Community (contributor diversity, bus factor), Discipline (CI, releases, license, templates), and Substance (code iteration density, growth). Returns a scored Markdown table and structured data. Requires GITHUB_TOKEN.',
|
|
278
|
+
schema: githubScoreParamsSchema,
|
|
279
|
+
outputSchema: githubScoreOutputSchema,
|
|
280
|
+
annotations: {
|
|
281
|
+
readOnlyHint: true,
|
|
282
|
+
idempotentHint: true,
|
|
283
|
+
destructiveHint: false,
|
|
284
|
+
openWorldHint: true
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
async (args, ctx) => {
|
|
288
|
+
if (!getCapabilities().github) {
|
|
289
|
+
return toToolResponse(toolFailure(getMissingEnvMessage("github")));
|
|
290
|
+
}
|
|
291
|
+
const reporter = createToolReporter(ctx, "github-score");
|
|
292
|
+
const result = await handleGitHubScore(args, reporter);
|
|
293
|
+
await reporter.progress(
|
|
294
|
+
100,
|
|
295
|
+
100,
|
|
296
|
+
result.isError ? "GitHub scoring failed" : "GitHub scoring complete"
|
|
297
|
+
);
|
|
298
|
+
return toToolResponse(result);
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
export {
|
|
303
|
+
handleGitHubScore,
|
|
304
|
+
registerGitHubScoreTool
|
|
305
|
+
};
|
|
306
|
+
//# sourceMappingURL=github-score.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/tools/github-score.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * GitHub Score Tool Handler\n * Searches GitHub repos by keywords, fetches detailed data, calculates quality metrics,\n * and returns a scored Markdown table.\n * NEVER throws \u2014 always returns structured response for graceful degradation.\n */\n\nimport type { MCPServer } from 'mcp-use/server';\n\nimport { getCapabilities, getMissingEnvMessage } from '../config/index.js';\nimport {\n githubScoreOutputSchema,\n githubScoreParamsSchema,\n type GitHubScoreParams,\n type GitHubScoreOutput,\n} from '../schemas/github-score.js';\nimport { GitHubClient, type RepoFullData } from '../clients/github.js';\nimport {\n scoreRepo,\n type DisciplineFlags,\n type RawRepoData,\n type CompositeResult,\n} from '../scoring/github-quality.js';\nimport { classifyError } from '../utils/errors.js';\nimport { mcpLog, formatError, formatDuration } from './utils.js';\nimport {\n createToolReporter,\n NOOP_REPORTER,\n toolFailure,\n toolSuccess,\n toToolResponse,\n type ToolExecutionResult,\n type ToolReporter,\n} from './mcp-helpers.js';\n\n// ============================================================================\n// Internal Types\n// ============================================================================\n\ninterface ScoredRepo {\n readonly fullName: string;\n readonly url: string;\n readonly stars: number;\n readonly forks: number;\n readonly openIssues: number;\n readonly language: string | null;\n readonly lastPush: string;\n readonly contributors: number;\n readonly archived: boolean;\n readonly score: CompositeResult;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction buildSearchQuery(params: GitHubScoreParams): string {\n let query: string;\n\n if (params.search_mode === 'OR') {\n query = params.keywords.join(' OR ');\n } else {\n // AND: space-separated terms\n query = params.keywords.join(' ');\n }\n\n if (params.min_stars > 0) {\n query += ` stars:>=${params.min_stars}`;\n }\n\n if (params.language) {\n query += ` language:${params.language}`;\n }\n\n return query;\n}\n\nfunction buildRawRepoData(\n fullData: RepoFullData,\n): RawRepoData {\n const g = fullData.graphql;\n\n const disciplineFlags: DisciplineFlags = {\n hasLicense: g.license !== null,\n hasContributing: g.hasContributing,\n hasIssueTemplate: g.hasIssueTemplate,\n hasPrTemplate: g.hasPrTemplate,\n hasCodeOfConduct: g.hasCodeOfConduct,\n hasCI: g.hasCI,\n hasReleases: g.totalReleases > 0,\n hasTopics: g.hasTopics,\n hasDescription: (g.description ?? '').length > 0,\n hasHomepage: (g.homepage ?? '').length > 0,\n };\n\n return {\n stars: g.stars,\n forks: g.forks,\n watchers: g.watchers,\n sizeKb: g.sizeKb,\n createdAt: new Date(g.createdAt),\n archived: g.archived,\n hasLicense: g.license !== null,\n closedIssues: g.closedIssues,\n totalIssues: g.openIssues + g.closedIssues,\n totalCommits: g.totalCommits,\n contributorCommits: fullData.contributors.map((c) => c.contributions),\n allWeeklyCommits: fullData.participation.all,\n ownerWeeklyCommits: fullData.participation.owner,\n disciplineFlags,\n };\n}\n\nfunction formatRelativeDate(isoDate: string): string {\n const date = new Date(isoDate);\n if (isNaN(date.getTime())) return 'unknown';\n\n const diffMs = Date.now() - date.getTime();\n const days = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (days < 1) return 'today';\n if (days === 1) return '1d ago';\n if (days < 30) return `${days}d ago`;\n if (days < 365) return `${Math.floor(days / 30)}mo ago`;\n return `${(days / 365).toFixed(1)}y ago`;\n}\n\nfunction formatStarCount(stars: number): string {\n if (stars >= 1000) {\n return `${(stars / 1000).toFixed(1)}k`;\n }\n return String(stars);\n}\n\nfunction scoreBand(score: number): string {\n if (score >= 70) return 'Excellent';\n if (score >= 50) return 'Good';\n if (score >= 30) return 'Meh';\n return 'Low';\n}\n\nfunction formatScoreTable(repos: ScoredRepo[]): string {\n let md = `| # | Repo | Stars | Score | Maint | Community | Discipline | Substance | Last Commit | Contributors | Flags |\\n`;\n md += `|---|------|-------|-------|-------|-----------|------------|-----------|-------------|--------------|-------|\\n`;\n\n repos.forEach((repo, i) => {\n const s = repo.score;\n const flagStr = s.flags.length > 0 ? s.flags.join(', ') : '-';\n md += `| ${i + 1} | [${repo.fullName}](${repo.url}) | ${formatStarCount(repo.stars)} | **${s.score}** | ${s.subScores.maintenance.toFixed(2)} | ${s.subScores.community.toFixed(2)} | ${s.subScores.discipline.toFixed(2)} | ${s.subScores.substance.toFixed(2)} | ${formatRelativeDate(repo.lastPush)} | ${repo.contributors} | ${flagStr} |\\n`;\n });\n\n return md;\n}\n\nfunction sortRepos(repos: ScoredRepo[], sortBy: string): ScoredRepo[] {\n const sorted = [...repos];\n switch (sortBy) {\n case 'stars':\n sorted.sort((a, b) => b.stars - a.stars);\n break;\n case 'updated':\n sorted.sort(\n (a, b) =>\n new Date(b.lastPush).getTime() - new Date(a.lastPush).getTime(),\n );\n break;\n case 'score':\n default:\n sorted.sort((a, b) => b.score.score - a.score.score);\n break;\n }\n return sorted;\n}\n\n// ============================================================================\n// Main Handler\n// ============================================================================\n\nexport async function handleGitHubScore(\n params: GitHubScoreParams,\n reporter: ToolReporter = NOOP_REPORTER,\n): Promise<ToolExecutionResult<GitHubScoreOutput>> {\n const startTime = Date.now();\n\n try {\n const client = new GitHubClient();\n const query = buildSearchQuery(params);\n\n mcpLog('info', `Searching GitHub: \"${query}\"`, 'github-score');\n await reporter.log('info', `Searching GitHub for: ${query}`);\n await reporter.progress(10, 100, 'Searching GitHub repositories');\n\n // 1. Search\n const searchResult = await client.searchRepos(query, 'stars', params.max_results);\n if (searchResult.error) {\n return buildGitHubScoreError(searchResult.error, query, startTime, client.apiCalls);\n }\n\n if (searchResult.items.length === 0) {\n const emptyMd = `# GitHub Score: No Results\\n\\nQuery: \\`${query}\\`\\n\\nNo repositories found. Try broader keywords or lower the min_stars filter.`;\n return toolSuccess(emptyMd, {\n content: emptyMd,\n metadata: {\n query,\n total_found: 0,\n total_scored: 0,\n failed_scores: 0,\n execution_time_ms: Date.now() - startTime,\n api_calls_made: client.apiCalls,\n },\n repos: [],\n });\n }\n\n mcpLog('info', `Found ${searchResult.totalCount} repos, scoring top ${searchResult.items.length}`, 'github-score');\n await reporter.progress(20, 100, `Found ${searchResult.items.length} repos, fetching details`);\n\n // 2. Fetch detailed data + score\n const repoRequests = searchResult.items.map((item) => ({\n owner: item.owner,\n name: item.name,\n }));\n\n const detailResults = await client.fetchMultipleRepoDetails(repoRequests);\n\n const scoredRepos: ScoredRepo[] = [];\n let failedCount = 0;\n\n for (let i = 0; i < detailResults.length; i++) {\n const detail = detailResults[i]!;\n const searchItem = searchResult.items[i]!;\n\n if (detail.error || !detail.data) {\n failedCount++;\n mcpLog('warning', `Failed to score ${searchItem.fullName}: ${detail.error?.message ?? 'unknown'}`, 'github-score');\n continue;\n }\n\n try {\n const rawData = buildRawRepoData(detail.data);\n const score = scoreRepo(rawData);\n\n scoredRepos.push({\n fullName: searchItem.fullName,\n url: searchItem.url,\n stars: detail.data.graphql.stars,\n forks: detail.data.graphql.forks,\n openIssues: detail.data.graphql.openIssues,\n language: detail.data.graphql.language,\n lastPush: detail.data.graphql.pushedAt,\n contributors: detail.data.contributors.length,\n archived: detail.data.graphql.archived,\n score,\n });\n } catch {\n failedCount++;\n mcpLog('warning', `Scoring error for ${searchItem.fullName}`, 'github-score');\n }\n\n // Progress: 20-90% range during scoring\n const progress = 20 + Math.round(((i + 1) / detailResults.length) * 70);\n await reporter.progress(progress, 100, `Scored ${i + 1}/${detailResults.length} repos`);\n }\n\n // 3. Sort\n const sorted = sortRepos(scoredRepos, params.sort);\n\n // 4. Build output\n const executionTime = Date.now() - startTime;\n const avgScore =\n sorted.length > 0\n ? Math.round(sorted.reduce((s, r) => s + r.score.score, 0) / sorted.length)\n : 0;\n\n let md = `# GitHub Score Report\\n\\n`;\n md += `**Query:** \\`${query}\\` | **Sorted by:** ${params.sort} | **Scored:** ${sorted.length}/${searchResult.items.length}`;\n if (failedCount > 0) md += ` | **Failed:** ${failedCount}`;\n md += `\\n\\n`;\n md += `**Average Score:** ${avgScore}/100 (${scoreBand(avgScore)})\\n\\n`;\n md += formatScoreTable(sorted);\n md += `\\n---\\n`;\n md += `*${formatDuration(executionTime)} | ${client.apiCalls} API calls | Score legend: Maint=Maintenance Pulse, Community=Community Health, Discipline=Engineering Practices, Substance=Code Quality*\\n`;\n md += `\\n**Score bands:** 70+ Excellent | 50-69 Good | 30-49 Meh | <30 Low\\n`;\n md += `\\n**Flag legend:** consistent-commits (steady work), growing (accelerating), active-community (external contributors), well-organized (CI/license/templates), high-bus-factor (multiple key contributors), stale-6mo (owner inactive), single-maintainer, no-license, archived, ai-dump-signal (few commits + large codebase)`;\n\n const structuredRepos = sorted.map((r) => ({\n full_name: r.fullName,\n url: r.url,\n stars: r.stars,\n forks: r.forks,\n open_issues: r.openIssues,\n language: r.language,\n last_push: r.lastPush,\n contributors: r.contributors,\n archived: r.archived,\n composite_score: r.score.score,\n maintenance_score: r.score.subScores.maintenance,\n community_score: r.score.subScores.community,\n discipline_score: r.score.subScores.discipline,\n substance_score: r.score.subScores.substance,\n flags: r.score.flags,\n }));\n\n return toolSuccess(md, {\n content: md,\n metadata: {\n query,\n total_found: searchResult.totalCount,\n total_scored: sorted.length,\n failed_scores: failedCount,\n execution_time_ms: executionTime,\n api_calls_made: client.apiCalls,\n },\n repos: structuredRepos,\n });\n } catch (error) {\n return buildGitHubScoreError(classifyError(error), buildSearchQuery(params), startTime, 0);\n }\n}\n\n// ============================================================================\n// Error Formatting\n// ============================================================================\n\nfunction buildGitHubScoreError(\n error: unknown,\n query: string,\n startTime: number,\n apiCalls: number,\n): ToolExecutionResult<GitHubScoreOutput> {\n const structuredError = typeof error === 'object' && error !== null && 'code' in error\n ? error as { code: string; message: string; retryable: boolean }\n : classifyError(error);\n\n mcpLog('error', `github-score: ${structuredError.message}`, 'github-score');\n\n const errorContent = formatError({\n code: structuredError.code,\n message: structuredError.message,\n retryable: structuredError.retryable,\n toolName: 'github-score',\n howToFix: [\n 'Verify GITHUB_TOKEN is set correctly',\n 'Check that the token has not expired',\n 'Ensure you have not exceeded the GitHub API rate limit (5000 req/hr)',\n ],\n alternatives: [\n 'web-search(keywords=[\"topic github\"]) \u2014 search the web for GitHub repos instead',\n ],\n });\n\n return toolFailure(\n `${errorContent}\\n\\nQuery: ${query}\\nExecution time: ${formatDuration(Date.now() - startTime)}\\nAPI calls: ${apiCalls}`,\n );\n}\n\n// ============================================================================\n// Tool Registration\n// ============================================================================\n\nexport function registerGitHubScoreTool(server: MCPServer): void {\n server.tool(\n {\n name: 'github-score',\n title: 'GitHub Score',\n description:\n 'Search GitHub repositories and score them with a composite \"Gives a Damn\" quality metric. ' +\n 'Takes keywords with AND/OR logic, fetches repo metadata, commit patterns, contributor data, ' +\n 'and CI/release/template presence to compute a 0-100 quality score across 4 dimensions: ' +\n 'Maintenance (commit consistency, velocity, owner activity), Community (contributor diversity, bus factor), ' +\n 'Discipline (CI, releases, license, templates), and Substance (code iteration density, growth). ' +\n 'Returns a scored Markdown table and structured data. Requires GITHUB_TOKEN.',\n schema: githubScoreParamsSchema,\n outputSchema: githubScoreOutputSchema,\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: true,\n },\n },\n async (args, ctx) => {\n if (!getCapabilities().github) {\n return toToolResponse(toolFailure(getMissingEnvMessage('github')));\n }\n\n const reporter = createToolReporter(ctx, 'github-score');\n const result = await handleGitHubScore(args, reporter);\n\n await reporter.progress(\n 100,\n 100,\n result.isError ? 'GitHub scoring failed' : 'GitHub scoring complete',\n );\n return toToolResponse(result);\n },\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type TypedCallToolResult } from 'mcp-use/server';
|
|
2
|
+
type ClientLogLevel = 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency';
|
|
3
|
+
interface ReporterContext {
|
|
4
|
+
log(level: ClientLogLevel, message: string, loggerName?: string): Promise<void>;
|
|
5
|
+
reportProgress?: (loaded: number, total?: number, message?: string) => Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export interface ToolExecutionSuccess<T extends Record<string, unknown>> {
|
|
8
|
+
readonly isError: false;
|
|
9
|
+
readonly content: string;
|
|
10
|
+
readonly structuredContent: T;
|
|
11
|
+
}
|
|
12
|
+
export interface ToolExecutionFailure {
|
|
13
|
+
readonly isError: true;
|
|
14
|
+
readonly content: string;
|
|
15
|
+
}
|
|
16
|
+
export type ToolExecutionResult<T extends Record<string, unknown>> = ToolExecutionSuccess<T> | ToolExecutionFailure;
|
|
17
|
+
export interface ToolReporter {
|
|
18
|
+
log(level: ClientLogLevel, message: string): Promise<void>;
|
|
19
|
+
progress(loaded: number, total?: number, message?: string): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export declare const NOOP_REPORTER: ToolReporter;
|
|
22
|
+
export declare function toolSuccess<T extends Record<string, unknown>>(content: string, structuredContent: T): ToolExecutionSuccess<T>;
|
|
23
|
+
export declare function toolFailure(content: string): ToolExecutionFailure;
|
|
24
|
+
export declare function createToolReporter(ctx: ReporterContext, loggerName: string): ToolReporter;
|
|
25
|
+
export declare function toToolResponse<T extends Record<string, unknown>>(result: ToolExecutionResult<T>): TypedCallToolResult<T> | TypedCallToolResult<never>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=mcp-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-helpers.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE3E,KAAK,cAAc,GACf,OAAO,GACP,MAAM,GACN,QAAQ,GACR,SAAS,GACT,OAAO,GACP,UAAU,GACV,OAAO,GACP,WAAW,CAAC;AAEhB,UAAU,eAAe;IACvB,GAAG,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChF,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACrE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,iBAAiB,EAAE,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAC7D,oBAAoB,CAAC,CAAC,CAAC,GACvB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E;AAED,eAAO,MAAM,aAAa,EAAE,YAG3B,CAAC;AAEF,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,OAAO,EAAE,MAAM,EACf,iBAAiB,EAAE,CAAC,GACnB,oBAAoB,CAAC,CAAC,CAAC,CAMzB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,oBAAoB,CAKjE;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,UAAU,EAAE,MAAM,GACjB,YAAY,CASd;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9D,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC7B,mBAAmB,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,KAAK,CAAC,CASrD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { error, markdown } from "mcp-use/server";
|
|
2
|
+
const NOOP_REPORTER = {
|
|
3
|
+
async log() {
|
|
4
|
+
},
|
|
5
|
+
async progress() {
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
function toolSuccess(content, structuredContent) {
|
|
9
|
+
return {
|
|
10
|
+
isError: false,
|
|
11
|
+
content,
|
|
12
|
+
structuredContent
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function toolFailure(content) {
|
|
16
|
+
return {
|
|
17
|
+
isError: true,
|
|
18
|
+
content
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function createToolReporter(ctx, loggerName) {
|
|
22
|
+
return {
|
|
23
|
+
log(level, message) {
|
|
24
|
+
return ctx.log(level, message, loggerName);
|
|
25
|
+
},
|
|
26
|
+
progress(loaded, total, message) {
|
|
27
|
+
return ctx.reportProgress?.(loaded, total, message) ?? Promise.resolve();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function toToolResponse(result) {
|
|
32
|
+
if (result.isError) {
|
|
33
|
+
return error(result.content);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
...markdown(result.content),
|
|
37
|
+
structuredContent: result.structuredContent
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
NOOP_REPORTER,
|
|
42
|
+
createToolReporter,
|
|
43
|
+
toToolResponse,
|
|
44
|
+
toolFailure,
|
|
45
|
+
toolSuccess
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=mcp-helpers.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/tools/mcp-helpers.ts"],
|
|
4
|
+
"sourcesContent": ["import { error, markdown, type TypedCallToolResult } from 'mcp-use/server';\n\ntype ClientLogLevel =\n | 'debug'\n | 'info'\n | 'notice'\n | 'warning'\n | 'error'\n | 'critical'\n | 'alert'\n | 'emergency';\n\ninterface ReporterContext {\n log(level: ClientLogLevel, message: string, loggerName?: string): Promise<void>;\n reportProgress?: (loaded: number, total?: number, message?: string) => Promise<void>;\n}\n\nexport interface ToolExecutionSuccess<T extends Record<string, unknown>> {\n readonly isError: false;\n readonly content: string;\n readonly structuredContent: T;\n}\n\nexport interface ToolExecutionFailure {\n readonly isError: true;\n readonly content: string;\n}\n\nexport type ToolExecutionResult<T extends Record<string, unknown>> =\n | ToolExecutionSuccess<T>\n | ToolExecutionFailure;\n\nexport interface ToolReporter {\n log(level: ClientLogLevel, message: string): Promise<void>;\n progress(loaded: number, total?: number, message?: string): Promise<void>;\n}\n\nexport const NOOP_REPORTER: ToolReporter = {\n async log() {},\n async progress() {},\n};\n\nexport function toolSuccess<T extends Record<string, unknown>>(\n content: string,\n structuredContent: T,\n): ToolExecutionSuccess<T> {\n return {\n isError: false,\n content,\n structuredContent,\n };\n}\n\nexport function toolFailure(content: string): ToolExecutionFailure {\n return {\n isError: true,\n content,\n };\n}\n\nexport function createToolReporter(\n ctx: ReporterContext,\n loggerName: string,\n): ToolReporter {\n return {\n log(level, message) {\n return ctx.log(level, message, loggerName);\n },\n progress(loaded, total, message) {\n return ctx.reportProgress?.(loaded, total, message) ?? Promise.resolve();\n },\n };\n}\n\nexport function toToolResponse<T extends Record<string, unknown>>(\n result: ToolExecutionResult<T>,\n): TypedCallToolResult<T> | TypedCallToolResult<never> {\n if (result.isError) {\n return error(result.content);\n }\n\n return {\n ...markdown(result.content),\n structuredContent: result.structuredContent,\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,OAAO,gBAA0C;AAqCnD,MAAM,gBAA8B;AAAA,EACzC,MAAM,MAAM;AAAA,EAAC;AAAA,EACb,MAAM,WAAW;AAAA,EAAC;AACpB;AAEO,SAAS,YACd,SACA,mBACyB;AACzB,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,YAAY,SAAuC;AACjE,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,mBACd,KACA,YACc;AACd,SAAO;AAAA,IACL,IAAI,OAAO,SAAS;AAClB,aAAO,IAAI,IAAI,OAAO,SAAS,UAAU;AAAA,IAC3C;AAAA,IACA,SAAS,QAAQ,OAAO,SAAS;AAC/B,aAAO,IAAI,iBAAiB,QAAQ,OAAO,OAAO,KAAK,QAAQ,QAAQ;AAAA,IACzE;AAAA,EACF;AACF;AAEO,SAAS,eACd,QACqD;AACrD,MAAI,OAAO,SAAS;AAClB,WAAO,MAAM,OAAO,OAAO;AAAA,EAC7B;AAEA,SAAO;AAAA,IACL,GAAG,SAAS,OAAO,OAAO;AAAA,IAC1B,mBAAmB,OAAO;AAAA,EAC5B;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reddit Tools - Search and Fetch
|
|
3
|
+
* NEVER throws - always returns structured response for graceful degradation
|
|
4
|
+
*/
|
|
5
|
+
import type { MCPServer } from 'mcp-use/server';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { type ToolExecutionResult, type ToolReporter } from './mcp-helpers.js';
|
|
8
|
+
export declare const searchRedditParamsSchema: z.ZodObject<{
|
|
9
|
+
queries: z.ZodArray<z.ZodString>;
|
|
10
|
+
date_after: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
export declare const getRedditPostParamsSchema: z.ZodObject<{
|
|
13
|
+
urls: z.ZodArray<z.ZodString>;
|
|
14
|
+
fetch_comments: z.ZodDefault<z.ZodBoolean>;
|
|
15
|
+
use_llm: z.ZodDefault<z.ZodBoolean>;
|
|
16
|
+
what_to_extract: z.ZodOptional<z.ZodString>;
|
|
17
|
+
}, z.core.$strict>;
|
|
18
|
+
export declare const searchRedditOutputSchema: z.ZodObject<{
|
|
19
|
+
content: z.ZodString;
|
|
20
|
+
metadata: z.ZodObject<{
|
|
21
|
+
query_count: z.ZodNumber;
|
|
22
|
+
total_results: z.ZodNumber;
|
|
23
|
+
date_after: z.ZodOptional<z.ZodString>;
|
|
24
|
+
}, z.core.$strict>;
|
|
25
|
+
}, z.core.$strict>;
|
|
26
|
+
type SearchRedditOutput = z.infer<typeof searchRedditOutputSchema>;
|
|
27
|
+
export declare const getRedditPostOutputSchema: z.ZodObject<{
|
|
28
|
+
content: z.ZodString;
|
|
29
|
+
metadata: z.ZodObject<{
|
|
30
|
+
total_urls: z.ZodNumber;
|
|
31
|
+
successful: z.ZodNumber;
|
|
32
|
+
failed: z.ZodNumber;
|
|
33
|
+
fetch_comments: z.ZodBoolean;
|
|
34
|
+
max_words_per_post: z.ZodNumber;
|
|
35
|
+
total_words_used: z.ZodNumber;
|
|
36
|
+
llm_requested: z.ZodBoolean;
|
|
37
|
+
llm_available: z.ZodBoolean;
|
|
38
|
+
llm_failures: z.ZodNumber;
|
|
39
|
+
total_batches: z.ZodNumber;
|
|
40
|
+
rate_limit_hits: z.ZodNumber;
|
|
41
|
+
}, z.core.$strict>;
|
|
42
|
+
}, z.core.$strict>;
|
|
43
|
+
type GetRedditPostOutput = z.infer<typeof getRedditPostOutputSchema>;
|
|
44
|
+
export declare function handleSearchReddit(queries: string[], apiKey: string, dateAfter?: string, reporter?: ToolReporter): Promise<ToolExecutionResult<SearchRedditOutput>>;
|
|
45
|
+
interface GetRedditPostsOptions {
|
|
46
|
+
fetchComments?: boolean;
|
|
47
|
+
use_llm?: boolean;
|
|
48
|
+
what_to_extract?: string;
|
|
49
|
+
}
|
|
50
|
+
export declare function handleGetRedditPosts(urls: string[], clientId: string, clientSecret: string, options?: GetRedditPostsOptions, reporter?: ToolReporter): Promise<ToolExecutionResult<GetRedditPostOutput>>;
|
|
51
|
+
export declare function registerSearchRedditTool(server: MCPServer): void;
|
|
52
|
+
export declare function registerGetRedditPostTool(server: MCPServer): void;
|
|
53
|
+
export {};
|
|
54
|
+
//# sourceMappingURL=reddit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reddit.d.ts","sourceRoot":"","sources":["../../../src/tools/reddit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgBxB,OAAO,EAML,KAAK,mBAAmB,EACxB,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAC;AAM1B,eAAO,MAAM,wBAAwB;;;kBAiB1B,CAAC;AAEZ,eAAO,MAAM,yBAAyB;;;;;kBAwB3B,CAAC;AAEZ,eAAO,MAAM,wBAAwB;;;;;;;kBAoB1B,CAAC;AAEZ,KAAK,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEnE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;kBAuD3B,CAAC;AAEZ,KAAK,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAgIrE,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,GAAE,YAA4B,GACrC,OAAO,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,CAAC,CA8BlD;AAMD,UAAU,qBAAqB;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAiND,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EAAE,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,qBAA0B,EACnC,QAAQ,GAAE,YAA4B,GACrC,OAAO,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC,CA6DnD;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6BhE;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAuCjE"}
|