@oss-scout/mcp 0.9.0 → 0.10.1
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/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp-server.bundle.cjs +1031 -321
- package/dist/tools.js +72 -12
- package/package.json +4 -4
package/dist/tools.js
CHANGED
|
@@ -3,6 +3,25 @@ import { SearchStrategySchema, ISSUE_URL_PATTERN, validateGitHubUrl, validateUrl
|
|
|
3
3
|
// Generous ceiling: even with inter-phase delays zeroed, vetting many issues
|
|
4
4
|
// under the budget tracker's sliding-window pacing can take minutes (#143).
|
|
5
5
|
const TOOL_TIMEOUT_MS = 300000;
|
|
6
|
+
// Surfaced on the JSON envelope when scout.checkpoint() returns false (#212).
|
|
7
|
+
// The skip tool reports this inline because it returns a short status line;
|
|
8
|
+
// the JSON tools (search, scout-features, config-set) carry it as a top-level
|
|
9
|
+
// field so a persist failure isn't silently swallowed.
|
|
10
|
+
const PERSIST_WARNING = "Checkpoint failed: changes were applied in memory but could not be persisted. They may be lost when the server restarts.";
|
|
11
|
+
/**
|
|
12
|
+
* Serialize a tool result, attaching a top-level `persistWarning` field when
|
|
13
|
+
* the preceding checkpoint failed (#212). Spreads onto the existing object so
|
|
14
|
+
* the happy-path payload is byte-for-byte unchanged.
|
|
15
|
+
*/
|
|
16
|
+
function withPersistWarning(payload, persisted) {
|
|
17
|
+
return persisted ? payload : { ...payload, persistWarning: PERSIST_WARNING };
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Rejects when `ms` elapses, but does NOT cancel the underlying work — the
|
|
21
|
+
* scout call keeps running (consuming API budget and mutating scout state)
|
|
22
|
+
* after the tool has returned its timeout error. Acceptable for a local
|
|
23
|
+
* stdio server; revisit with AbortSignal threading if it ever matters (#295).
|
|
24
|
+
*/
|
|
6
25
|
function withTimeout(promise, ms = TOOL_TIMEOUT_MS) {
|
|
7
26
|
let timeoutId;
|
|
8
27
|
const timer = new Promise((_, reject) => {
|
|
@@ -14,8 +33,11 @@ export function registerTools(server, scout) {
|
|
|
14
33
|
server.tool("search", "Search for open source issues matching your preferences", {
|
|
15
34
|
maxResults: z
|
|
16
35
|
.number()
|
|
36
|
+
.int()
|
|
37
|
+
.min(1)
|
|
38
|
+
.max(50)
|
|
17
39
|
.optional()
|
|
18
|
-
.describe("Maximum number of results to return (default 10)"),
|
|
40
|
+
.describe("Maximum number of results to return (1-50, default 10)"),
|
|
19
41
|
strategies: z
|
|
20
42
|
.string()
|
|
21
43
|
.optional()
|
|
@@ -28,24 +50,41 @@ export function registerTools(server, scout) {
|
|
|
28
50
|
.array(z.string())
|
|
29
51
|
.optional()
|
|
30
52
|
.describe("Soft-boost ranking for candidates in one of these `owner/repo` slugs. Stronger weight than language match (#1244). Not a filter; only reorders."),
|
|
53
|
+
avoidRepos: z
|
|
54
|
+
.array(z.string())
|
|
55
|
+
.optional()
|
|
56
|
+
.describe("Soft PENALTY (milder than excludeRepos) for candidates in one of these `owner/repo` slugs (#168). Pushes them down the ranking but does not remove them; a strong boost can still outweigh it."),
|
|
57
|
+
boostIssueTypes: z
|
|
58
|
+
.array(z.string())
|
|
59
|
+
.optional()
|
|
60
|
+
.describe('Soft-boost ranking for candidates whose issue labels match one of these types, case-insensitive (e.g. "bug", "good first issue") (#168). Not a filter; only reorders.'),
|
|
31
61
|
diversityRatio: z
|
|
32
62
|
.number()
|
|
33
63
|
.min(0)
|
|
34
64
|
.max(1)
|
|
35
65
|
.optional()
|
|
36
66
|
.describe("Diversity counterweight (#1244): fraction of result slots reserved for candidates that matched NEITHER preference list. Counters echo-chamber bias when boosts accumulate over time. 0 disables, 1 reserves every slot for diversity. Default 0."),
|
|
37
|
-
}, async ({ maxResults, strategies, preferLanguages, preferRepos, diversityRatio, }) => {
|
|
67
|
+
}, async ({ maxResults, strategies, preferLanguages, preferRepos, avoidRepos, boostIssueTypes, diversityRatio, }) => {
|
|
38
68
|
try {
|
|
69
|
+
// Validate each CSV entry with a readable error instead of letting a
|
|
70
|
+
// raw ZodError string reach the model (#295).
|
|
39
71
|
const parsedStrategies = strategies
|
|
40
|
-
? strategies
|
|
41
|
-
.
|
|
42
|
-
|
|
72
|
+
? strategies.split(",").map((s) => {
|
|
73
|
+
const trimmed = s.trim();
|
|
74
|
+
const parsed = SearchStrategySchema.safeParse(trimmed);
|
|
75
|
+
if (!parsed.success) {
|
|
76
|
+
throw new Error(`Invalid strategy "${trimmed}". Valid strategies: ${SearchStrategySchema.options.join(", ")}`);
|
|
77
|
+
}
|
|
78
|
+
return parsed.data;
|
|
79
|
+
})
|
|
43
80
|
: undefined;
|
|
44
81
|
const result = await withTimeout(scout.search({
|
|
45
82
|
maxResults: maxResults ?? 10,
|
|
46
83
|
strategies: parsedStrategies,
|
|
47
84
|
preferLanguages,
|
|
48
85
|
preferRepos,
|
|
86
|
+
avoidRepos,
|
|
87
|
+
boostIssueTypes,
|
|
49
88
|
diversityRatio,
|
|
50
89
|
// No fixed inter-phase sleeps in the request/response MCP context;
|
|
51
90
|
// the budget tracker still paces the GitHub calls (#143)
|
|
@@ -53,9 +92,14 @@ export function registerTools(server, scout) {
|
|
|
53
92
|
broadPhaseDelayMs: 0,
|
|
54
93
|
}));
|
|
55
94
|
scout.saveResults(result.candidates);
|
|
56
|
-
await scout.checkpoint();
|
|
95
|
+
const persisted = await scout.checkpoint();
|
|
57
96
|
return {
|
|
58
|
-
content: [
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: "text",
|
|
100
|
+
text: JSON.stringify(withPersistWarning(result, persisted), null, 2),
|
|
101
|
+
},
|
|
102
|
+
],
|
|
59
103
|
};
|
|
60
104
|
}
|
|
61
105
|
catch (err) {
|
|
@@ -69,8 +113,11 @@ export function registerTools(server, scout) {
|
|
|
69
113
|
server.tool("scout-features", "Surface feature-scoped contribution opportunities in repos where you have 3+ merged PRs", {
|
|
70
114
|
maxResults: z
|
|
71
115
|
.number()
|
|
116
|
+
.int()
|
|
117
|
+
.min(1)
|
|
118
|
+
.max(50)
|
|
72
119
|
.optional()
|
|
73
|
-
.describe("Maximum number of results to return (default 10)"),
|
|
120
|
+
.describe("Maximum number of results to return (1-50, default 10)"),
|
|
74
121
|
anchorThreshold: z
|
|
75
122
|
.number()
|
|
76
123
|
.int()
|
|
@@ -92,9 +139,14 @@ export function registerTools(server, scout) {
|
|
|
92
139
|
splitRatio,
|
|
93
140
|
}));
|
|
94
141
|
scout.saveResults([...result.quickWins, ...result.biggerBets]);
|
|
95
|
-
await scout.checkpoint();
|
|
142
|
+
const persisted = await scout.checkpoint();
|
|
96
143
|
return {
|
|
97
|
-
content: [
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: "text",
|
|
147
|
+
text: JSON.stringify(withPersistWarning(result, persisted), null, 2),
|
|
148
|
+
},
|
|
149
|
+
],
|
|
98
150
|
};
|
|
99
151
|
}
|
|
100
152
|
catch (err) {
|
|
@@ -192,6 +244,9 @@ export function registerTools(server, scout) {
|
|
|
192
244
|
};
|
|
193
245
|
}
|
|
194
246
|
if (action === "remove") {
|
|
247
|
+
// Deliberately no URL validation, matching the CLI's runSkipRemove:
|
|
248
|
+
// entries stored before skip-add validation existed may be junk,
|
|
249
|
+
// and exact-match removal is the only way to clean them up.
|
|
195
250
|
const wasPresent = scout
|
|
196
251
|
.getSkippedIssues()
|
|
197
252
|
.some((s) => s.url === issueUrl);
|
|
@@ -279,10 +334,15 @@ export function registerTools(server, scout) {
|
|
|
279
334
|
scout.updatePreferences({
|
|
280
335
|
[key]: validated[key],
|
|
281
336
|
});
|
|
282
|
-
await scout.checkpoint();
|
|
337
|
+
const persisted = await scout.checkpoint();
|
|
283
338
|
const updated = scout.getPreferences();
|
|
284
339
|
return {
|
|
285
|
-
content: [
|
|
340
|
+
content: [
|
|
341
|
+
{
|
|
342
|
+
type: "text",
|
|
343
|
+
text: JSON.stringify(withPersistWarning(updated, persisted), null, 2),
|
|
344
|
+
},
|
|
345
|
+
],
|
|
286
346
|
};
|
|
287
347
|
});
|
|
288
348
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oss-scout/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "MCP server for oss-scout — exposes issue discovery and vetting as MCP tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"
|
|
12
|
-
"
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
20
20
|
"zod": "^4.3.6",
|
|
21
|
-
"@oss-scout/core": "^1.
|
|
21
|
+
"@oss-scout/core": "^1.5.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^25.5.0",
|