@oss-scout/mcp 0.1.0 → 0.2.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.
@@ -1,3 +1,3 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { OssScout } from '@oss-scout/core';
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { OssScout } from "@oss-scout/core";
3
3
  export declare function registerResources(server: McpServer, scout: OssScout): void;
package/dist/resources.js CHANGED
@@ -1,26 +1,32 @@
1
1
  export function registerResources(server, scout) {
2
- server.resource('config', 'scout://config', { description: 'Current oss-scout preferences' }, async () => ({
3
- contents: [{
4
- uri: 'scout://config',
5
- mimeType: 'application/json',
2
+ server.resource("config", "scout://config", { description: "Current oss-scout preferences" }, async () => ({
3
+ contents: [
4
+ {
5
+ uri: "scout://config",
6
+ mimeType: "application/json",
6
7
  text: JSON.stringify(scout.getPreferences(), null, 2),
7
- }],
8
+ },
9
+ ],
8
10
  }));
9
- server.resource('results', 'scout://results', { description: 'Saved search results from previous runs' }, async () => ({
10
- contents: [{
11
- uri: 'scout://results',
12
- mimeType: 'application/json',
11
+ server.resource("results", "scout://results", { description: "Saved search results from previous runs" }, async () => ({
12
+ contents: [
13
+ {
14
+ uri: "scout://results",
15
+ mimeType: "application/json",
13
16
  text: JSON.stringify(scout.getSavedResults(), null, 2),
14
- }],
17
+ },
18
+ ],
15
19
  }));
16
- server.resource('scores', 'scout://scores', { description: 'Repository score data based on contribution history' }, async () => {
20
+ server.resource("scores", "scout://scores", { description: "Repository score data based on contribution history" }, async () => {
17
21
  const state = scout.getState();
18
22
  return {
19
- contents: [{
20
- uri: 'scout://scores',
21
- mimeType: 'application/json',
23
+ contents: [
24
+ {
25
+ uri: "scout://scores",
26
+ mimeType: "application/json",
22
27
  text: JSON.stringify(state.repoScores, null, 2),
23
- }],
28
+ },
29
+ ],
24
30
  };
25
31
  });
26
32
  }
package/dist/tools.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { OssScout } from '@oss-scout/core';
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { OssScout } from "@oss-scout/core";
3
3
  export declare function registerTools(server: McpServer, scout: OssScout): void;
package/dist/tools.js CHANGED
@@ -1,43 +1,99 @@
1
- import { z } from 'zod';
2
- import { SearchStrategySchema, ScoutPreferencesSchema } from '@oss-scout/core';
1
+ import { z } from "zod";
2
+ import { SearchStrategySchema, ScoutPreferencesSchema } from "@oss-scout/core";
3
3
  export function registerTools(server, scout) {
4
- server.tool('search', 'Search for open source issues matching your preferences', {
5
- maxResults: z.number().optional().describe('Maximum number of results to return (default 10)'),
6
- strategies: z.string().optional().describe('Comma-separated search strategies: merged, orgs, starred, broad, maintained, all'),
4
+ server.tool("search", "Search for open source issues matching your preferences", {
5
+ maxResults: z
6
+ .number()
7
+ .optional()
8
+ .describe("Maximum number of results to return (default 10)"),
9
+ strategies: z
10
+ .string()
11
+ .optional()
12
+ .describe("Comma-separated search strategies: merged, orgs, starred, broad, maintained, all"),
7
13
  }, async ({ maxResults, strategies }) => {
8
- const parsedStrategies = strategies
9
- ? strategies.split(',').map((s) => SearchStrategySchema.parse(s.trim()))
10
- : undefined;
11
- const result = await scout.search({
12
- maxResults: maxResults ?? 10,
13
- strategies: parsedStrategies,
14
- });
15
- return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
14
+ try {
15
+ const parsedStrategies = strategies
16
+ ? strategies
17
+ .split(",")
18
+ .map((s) => SearchStrategySchema.parse(s.trim()))
19
+ : undefined;
20
+ const result = await scout.search({
21
+ maxResults: maxResults ?? 10,
22
+ strategies: parsedStrategies,
23
+ });
24
+ scout.saveResults(result.candidates);
25
+ await scout.checkpoint();
26
+ return {
27
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
28
+ };
29
+ }
30
+ catch (err) {
31
+ const msg = err instanceof Error ? err.message : String(err);
32
+ return {
33
+ content: [{ type: "text", text: `Error: ${msg}` }],
34
+ isError: true,
35
+ };
36
+ }
16
37
  });
17
- server.tool('vet', 'Vet a specific GitHub issue for contribution viability', {
18
- issueUrl: z.string().describe('Full GitHub issue URL (e.g. https://github.com/owner/repo/issues/123)'),
38
+ server.tool("vet", "Vet a specific GitHub issue for contribution viability", {
39
+ issueUrl: z
40
+ .string()
41
+ .describe("Full GitHub issue URL (e.g. https://github.com/owner/repo/issues/123)"),
19
42
  }, async ({ issueUrl }) => {
20
- const candidate = await scout.vetIssue(issueUrl);
21
- return { content: [{ type: 'text', text: JSON.stringify(candidate, null, 2) }] };
43
+ try {
44
+ const candidate = await scout.vetIssue(issueUrl);
45
+ return {
46
+ content: [{ type: "text", text: JSON.stringify(candidate, null, 2) }],
47
+ };
48
+ }
49
+ catch (err) {
50
+ const msg = err instanceof Error ? err.message : String(err);
51
+ return {
52
+ content: [{ type: "text", text: `Error: ${msg}` }],
53
+ isError: true,
54
+ };
55
+ }
22
56
  });
23
- server.tool('config', 'Show current oss-scout configuration preferences', async () => {
57
+ server.tool("config", "Show current oss-scout configuration preferences", async () => {
24
58
  const preferences = scout.getPreferences();
25
- return { content: [{ type: 'text', text: JSON.stringify(preferences, null, 2) }] };
59
+ return {
60
+ content: [{ type: "text", text: JSON.stringify(preferences, null, 2) }],
61
+ };
26
62
  });
27
63
  const VALID_KEYS = Object.keys(ScoutPreferencesSchema.shape);
28
64
  const ARRAY_KEYS = new Set([
29
- 'languages', 'labels', 'preferredOrgs', 'projectCategories',
30
- 'excludeRepos', 'aiPolicyBlocklist', 'scope', 'defaultStrategy',
65
+ "languages",
66
+ "labels",
67
+ "preferredOrgs",
68
+ "projectCategories",
69
+ "excludeRepos",
70
+ "excludeOrgs",
71
+ "aiPolicyBlocklist",
72
+ "scope",
73
+ "defaultStrategy",
74
+ ]);
75
+ const NUMBER_KEYS = new Set([
76
+ "minStars",
77
+ "maxIssueAgeDays",
78
+ "minRepoScoreThreshold",
31
79
  ]);
32
- const NUMBER_KEYS = new Set(['minStars', 'maxIssueAgeDays', 'minRepoScoreThreshold']);
33
- const BOOLEAN_KEYS = new Set(['includeDocIssues']);
34
- server.tool('config-set', 'Update an oss-scout configuration preference', {
35
- key: z.string().describe('Preference key to update (e.g. languages, minStars, excludeRepos)'),
36
- value: z.string().describe('New value — comma-separated for arrays, plain string for scalars'),
80
+ const BOOLEAN_KEYS = new Set(["includeDocIssues"]);
81
+ server.tool("config-set", "Update an oss-scout configuration preference", {
82
+ key: z
83
+ .string()
84
+ .describe("Preference key to update (e.g. languages, minStars, excludeRepos)"),
85
+ value: z
86
+ .string()
87
+ .describe("New value — comma-separated for arrays, plain string for scalars"),
37
88
  }, async ({ key, value }) => {
38
89
  if (!VALID_KEYS.includes(key)) {
39
90
  return {
40
- content: [{ type: 'text', text: `Unknown config key: "${key}". Valid keys: ${VALID_KEYS.join(', ')}` }],
91
+ content: [
92
+ {
93
+ type: "text",
94
+ text: `Unknown config key: "${key}". Valid keys: ${VALID_KEYS.join(", ")}`,
95
+ },
96
+ ],
41
97
  isError: true,
42
98
  };
43
99
  }
@@ -47,18 +103,26 @@ export function registerTools(server, scout) {
47
103
  try {
48
104
  parsed = JSON.parse(value);
49
105
  if (!Array.isArray(parsed)) {
50
- parsed = value.split(',').map((s) => s.trim()).filter(Boolean);
106
+ parsed = value
107
+ .split(",")
108
+ .map((s) => s.trim())
109
+ .filter(Boolean);
51
110
  }
52
111
  }
53
112
  catch {
54
- parsed = value.split(',').map((s) => s.trim()).filter(Boolean);
113
+ parsed = value
114
+ .split(",")
115
+ .map((s) => s.trim())
116
+ .filter(Boolean);
55
117
  }
56
118
  }
57
119
  else if (NUMBER_KEYS.has(key)) {
58
120
  const num = Number(value);
59
121
  if (isNaN(num)) {
60
122
  return {
61
- content: [{ type: 'text', text: `Invalid number for "${key}": "${value}"` }],
123
+ content: [
124
+ { type: "text", text: `Invalid number for "${key}": "${value}"` },
125
+ ],
62
126
  isError: true,
63
127
  };
64
128
  }
@@ -66,13 +130,18 @@ export function registerTools(server, scout) {
66
130
  }
67
131
  else if (BOOLEAN_KEYS.has(key)) {
68
132
  const lower = value.toLowerCase();
69
- if (lower === 'true' || lower === 'yes')
133
+ if (lower === "true" || lower === "yes")
70
134
  parsed = true;
71
- else if (lower === 'false' || lower === 'no')
135
+ else if (lower === "false" || lower === "no")
72
136
  parsed = false;
73
137
  else {
74
138
  return {
75
- content: [{ type: 'text', text: `Invalid boolean for "${key}": "${value}". Use true/false or yes/no.` }],
139
+ content: [
140
+ {
141
+ type: "text",
142
+ text: `Invalid boolean for "${key}": "${value}". Use true/false or yes/no.`,
143
+ },
144
+ ],
76
145
  isError: true,
77
146
  };
78
147
  }
@@ -86,14 +155,19 @@ export function registerTools(server, scout) {
86
155
  // Validate the full preferences object with Zod
87
156
  const result = ScoutPreferencesSchema.safeParse(candidate);
88
157
  if (!result.success) {
89
- const issues = result.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ');
158
+ const issues = result.error.issues
159
+ .map((i) => `${i.path.join(".")}: ${i.message}`)
160
+ .join("; ");
90
161
  return {
91
- content: [{ type: 'text', text: `Validation error: ${issues}` }],
162
+ content: [{ type: "text", text: `Validation error: ${issues}` }],
92
163
  isError: true,
93
164
  };
94
165
  }
95
166
  scout.updatePreferences({ [key]: parsed });
167
+ await scout.checkpoint();
96
168
  const updated = scout.getPreferences();
97
- return { content: [{ type: 'text', text: JSON.stringify(updated, null, 2) }] };
169
+ return {
170
+ content: [{ type: "text", text: JSON.stringify(updated, null, 2) }],
171
+ };
98
172
  });
99
173
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oss-scout/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "MCP server for oss-scout — exposes issue discovery and vetting as MCP tools",
5
5
  "type": "module",
6
6
  "bin": {