octocode-mcp 1.0.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/LICENSE.md +21 -0
- package/README.md +259 -0
- package/build/index.js +2371 -0
- package/package.json +81 -0
package/build/index.js
ADDED
|
@@ -0,0 +1,2371 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import z from 'zod';
|
|
5
|
+
import { exec, execSync } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
|
+
import NodeCache from 'node-cache';
|
|
8
|
+
import crypto from 'crypto';
|
|
9
|
+
|
|
10
|
+
const TOOL_NAMES = {
|
|
11
|
+
SEARCH_GITHUB_CODE: 'search_github_code',
|
|
12
|
+
FETCH_GITHUB_FILE_CONTENT: 'fetch_github_file_content',
|
|
13
|
+
VIEW_REPOSITORY: 'view_repository',
|
|
14
|
+
NPM_VIEW: 'npm_view',
|
|
15
|
+
SEARCH_GITHUB_REPOS: 'search_github_repos',
|
|
16
|
+
SEARCH_GITHUB_COMMITS: 'search_github_commits',
|
|
17
|
+
SEARCH_GITHUB_PULL_REQUESTS: 'search_github_pull_requests',
|
|
18
|
+
GET_USER_ORGANIZATIONS: 'get_user_organizations',
|
|
19
|
+
NPM_SEARCH: 'npm_search',
|
|
20
|
+
VIEW_REPOSITORY_STRUCTURE: 'view_repository_structure',
|
|
21
|
+
SEARCH_GITHUB_ISSUES: 'search_github_issues',
|
|
22
|
+
SEARCH_GITHUB_DISCUSSIONS: 'search_github_discussions',
|
|
23
|
+
SEARCH_GITHUB_TOPICS: 'search_github_topics',
|
|
24
|
+
SEARCH_GITHUB_USERS: 'search_github_users',
|
|
25
|
+
};
|
|
26
|
+
const INDEX_MAP = {
|
|
27
|
+
CRITICAL: '#CRITICAL#',
|
|
28
|
+
WARNING: '#WARNING#',
|
|
29
|
+
CODE: '#CODE#',
|
|
30
|
+
VALIDATION: '#VALIDATION#',
|
|
31
|
+
USER: '#USER#',
|
|
32
|
+
EFFICIENCY: '#EFFICIENCY#',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const PROMPT_SYSTEM_PROMPT = `Expert code discovery assistant with advanced reasoning capabilities. Find production-ready code examples from GitHub/npm using sophisticated research workflows.
|
|
36
|
+
|
|
37
|
+
## ${INDEX_MAP.CRITICAL} CORE REQUIREMENTS
|
|
38
|
+
- ${INDEX_MAP.CODE} **3+ COMPLETE CODE EXAMPLES** (20+ lines) with syntax highlighting
|
|
39
|
+
- ${INDEX_MAP.CODE} **REPOSITORY CITATIONS**: \`\`\`language:owner/repo/filepath
|
|
40
|
+
- ${INDEX_MAP.CODE} **WORKING IMPLEMENTATIONS** with imports/exports/context
|
|
41
|
+
- ${INDEX_MAP.CODE} **REPOSITORY LINKS** for each example
|
|
42
|
+
|
|
43
|
+
## ${INDEX_MAP.EFFICIENCY} ADVANCED RESEARCH METHODOLOGY
|
|
44
|
+
|
|
45
|
+
**🧠 CHAIN-OF-THOUGHT REASONING:**
|
|
46
|
+
1. **UNDERSTAND INTENT**: Analyze user query complexity and extract core requirements
|
|
47
|
+
2. **DECOMPOSE PROBLEM**: Break complex queries into searchable components
|
|
48
|
+
3. **HYPOTHESIS FORMATION**: Predict likely implementation patterns and technologies
|
|
49
|
+
4. **EVIDENCE GATHERING**: Execute targeted searches to validate/refute hypotheses
|
|
50
|
+
5. **SYNTHESIS**: Combine findings into coherent, actionable insights
|
|
51
|
+
6. **VALIDATION**: Cross-reference multiple sources for accuracy
|
|
52
|
+
|
|
53
|
+
**🔍 MULTI-STEP RESEARCH FLOWS:**
|
|
54
|
+
|
|
55
|
+
**EXPLORATORY DISCOVERY:**
|
|
56
|
+
1. ${TOOL_NAMES.SEARCH_GITHUB_TOPICS} → Semantic landscape mapping
|
|
57
|
+
2. ${TOOL_NAMES.SEARCH_GITHUB_REPOS} → Repository ecosystem analysis
|
|
58
|
+
3. ${TOOL_NAMES.SEARCH_GITHUB_CODE} → Implementation pattern discovery
|
|
59
|
+
4. Cross-validation via ${TOOL_NAMES.SEARCH_GITHUB_ISSUES} + ${TOOL_NAMES.SEARCH_GITHUB_PULL_REQUESTS}
|
|
60
|
+
|
|
61
|
+
**DEEP IMPLEMENTATION ANALYSIS:**
|
|
62
|
+
1. ${TOOL_NAMES.VIEW_REPOSITORY} → Context establishment
|
|
63
|
+
2. ${TOOL_NAMES.VIEW_REPOSITORY_STRUCTURE} → Architecture understanding
|
|
64
|
+
3. ${TOOL_NAMES.FETCH_GITHUB_FILE_CONTENT} → Core implementation extraction
|
|
65
|
+
4. ${TOOL_NAMES.SEARCH_GITHUB_COMMITS} → Evolution tracking
|
|
66
|
+
|
|
67
|
+
**COMPARATIVE RESEARCH:**
|
|
68
|
+
1. Parallel repository analysis across multiple solutions
|
|
69
|
+
2. Cross-reference implementation approaches
|
|
70
|
+
3. Analyze trade-offs via issue/PR discussions
|
|
71
|
+
4. Synthesize best practices from multiple sources
|
|
72
|
+
|
|
73
|
+
**ORGANIZATIONAL INTELLIGENCE:**
|
|
74
|
+
- **Auto-trigger ${TOOL_NAMES.GET_USER_ORGANIZATIONS}** when company context detected
|
|
75
|
+
- **Prioritize internal repositories** for relevant findings
|
|
76
|
+
- **Cross-organizational pattern analysis** for knowledge transfer
|
|
77
|
+
|
|
78
|
+
## ${INDEX_MAP.VALIDATION} ADAPTIVE SEARCH STRATEGIES
|
|
79
|
+
|
|
80
|
+
**PROGRESSIVE COMPLEXITY:**
|
|
81
|
+
- **Simple Query**: Single tool → immediate results
|
|
82
|
+
- **Medium Query**: 2-3 tools → comparative analysis
|
|
83
|
+
- **Complex Query**: Full research flow → comprehensive investigation
|
|
84
|
+
|
|
85
|
+
**DYNAMIC STOPPING CRITERIA:**
|
|
86
|
+
- **High Confidence**: 3+ quality examples with validation
|
|
87
|
+
- **Medium Confidence**: Continue with related searches
|
|
88
|
+
- **Low Confidence**: Expand search scope or acknowledge limitations
|
|
89
|
+
|
|
90
|
+
**QUALITY VALIDATION PIPELINE:**
|
|
91
|
+
1. **Repository Quality**: >1K stars OR recent activity OR enterprise usage
|
|
92
|
+
2. **Code Quality**: Production patterns, error handling, documentation
|
|
93
|
+
3. **Cross-Validation**: Multiple sources confirm patterns
|
|
94
|
+
4. **Recency**: Prefer modern implementations and active maintenance
|
|
95
|
+
|
|
96
|
+
## ${INDEX_MAP.USER} SOPHISTICATED RESPONSE STRATEGIES
|
|
97
|
+
|
|
98
|
+
**REASONING TRANSPARENCY:**
|
|
99
|
+
- Show hypothesis formation and validation process
|
|
100
|
+
- Explain search strategy decisions and pivots
|
|
101
|
+
- Highlight confidence levels and evidence strength
|
|
102
|
+
|
|
103
|
+
**CONTEXTUAL ADAPTATION:**
|
|
104
|
+
- **Beginner-friendly**: Include learning context and explanations
|
|
105
|
+
- **Expert-level**: Focus on nuanced implementation details
|
|
106
|
+
- **Organizational**: Emphasize internal patterns and knowledge transfer
|
|
107
|
+
|
|
108
|
+
**MULTI-PERSPECTIVE ANALYSIS:**
|
|
109
|
+
- **Technical**: Implementation details and architecture
|
|
110
|
+
- **Strategic**: Adoption patterns and ecosystem trends
|
|
111
|
+
- **Practical**: Real-world usage and gotchas
|
|
112
|
+
|
|
113
|
+
**WORKING CODE EXAMPLES:**
|
|
114
|
+
\`\`\`language:owner/repo/filepath
|
|
115
|
+
// Complete implementation with reasoning context
|
|
116
|
+
// Why this approach? What alternatives exist?
|
|
117
|
+
// Production considerations and trade-offs
|
|
118
|
+
\`\`\`
|
|
119
|
+
|
|
120
|
+
**CONSTRAINTS & QUALITY GATES:**
|
|
121
|
+
- Extract code from results with implementation context
|
|
122
|
+
- Provide reasoning for tool selection and search strategies
|
|
123
|
+
- Acknowledge uncertainty and suggest follow-up investigations
|
|
124
|
+
- Prioritize user organization repos when relevant
|
|
125
|
+
|
|
126
|
+
**DO NOT PROVIDE:**
|
|
127
|
+
- ${INDEX_MAP.WARNING} Repository lists without extracted implementations
|
|
128
|
+
- ${INDEX_MAP.WARNING} Descriptions without working examples
|
|
129
|
+
- ${INDEX_MAP.WARNING} Single-source conclusions without validation
|
|
130
|
+
- ${INDEX_MAP.WARNING} Recommendations without evidence-based reasoning`;
|
|
131
|
+
|
|
132
|
+
const execAsync = promisify(exec);
|
|
133
|
+
const cache = new NodeCache({
|
|
134
|
+
stdTTL: 3600, // 1 hour cache
|
|
135
|
+
checkperiod: 600, // Check for expired keys every 10 minutes
|
|
136
|
+
});
|
|
137
|
+
function generateCacheKey(prefix, params) {
|
|
138
|
+
const paramString = JSON.stringify(params, Object.keys(params).sort());
|
|
139
|
+
const hash = crypto.createHash('md5').update(paramString).digest('hex');
|
|
140
|
+
return `${prefix}:${hash}`;
|
|
141
|
+
}
|
|
142
|
+
async function withCache(cacheKey, operation) {
|
|
143
|
+
// Check if result exists in cache
|
|
144
|
+
const cachedResult = cache.get(cacheKey);
|
|
145
|
+
if (cachedResult) {
|
|
146
|
+
return cachedResult;
|
|
147
|
+
}
|
|
148
|
+
// Execute operation
|
|
149
|
+
const result = await operation();
|
|
150
|
+
// Only cache successful responses
|
|
151
|
+
if (!result.isError) {
|
|
152
|
+
cache.set(cacheKey, result);
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
async function searchGitHubCode(params) {
|
|
157
|
+
const cacheKey = generateCacheKey('gh-code', params);
|
|
158
|
+
return withCache(cacheKey, async () => {
|
|
159
|
+
try {
|
|
160
|
+
const command = buildGitHubCodeSearchCommand(params);
|
|
161
|
+
const content = execSync(command, { encoding: 'utf-8' });
|
|
162
|
+
const result = {
|
|
163
|
+
searchType: 'code',
|
|
164
|
+
query: params.query || '',
|
|
165
|
+
results: content,
|
|
166
|
+
rawOutput: content,
|
|
167
|
+
};
|
|
168
|
+
return createSuccessResult(result);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
return createErrorResult('Failed to search GitHub code', error);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
async function searchGitHubCommits(params) {
|
|
176
|
+
const cacheKey = generateCacheKey('gh-commits', params);
|
|
177
|
+
return withCache(cacheKey, async () => {
|
|
178
|
+
try {
|
|
179
|
+
const command = buildGitHubCommitsSearchCommand(params);
|
|
180
|
+
const content = execSync(command, { encoding: 'utf-8' });
|
|
181
|
+
const result = {
|
|
182
|
+
searchType: 'commits',
|
|
183
|
+
query: params.query || '',
|
|
184
|
+
results: content,
|
|
185
|
+
rawOutput: content,
|
|
186
|
+
};
|
|
187
|
+
return createSuccessResult(result);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
return createErrorResult('Failed to search GitHub commits', error);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
async function searchGitHubPullRequests(params) {
|
|
195
|
+
const cacheKey = generateCacheKey('gh-prs', params);
|
|
196
|
+
return withCache(cacheKey, async () => {
|
|
197
|
+
try {
|
|
198
|
+
const command = buildGitHubPullRequestsSearchCommand(params);
|
|
199
|
+
const content = execSync(command, { encoding: 'utf-8' });
|
|
200
|
+
const result = {
|
|
201
|
+
searchType: 'prs',
|
|
202
|
+
query: params.query || '',
|
|
203
|
+
results: content,
|
|
204
|
+
rawOutput: content,
|
|
205
|
+
};
|
|
206
|
+
return createSuccessResult(result);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
return createErrorResult('Failed to search GitHub pull requests', error);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
async function searchGitHubRepos(params) {
|
|
214
|
+
const cacheKey = generateCacheKey('gh-repos', params);
|
|
215
|
+
return withCache(cacheKey, async () => {
|
|
216
|
+
try {
|
|
217
|
+
const command = buildGitHubReposSearchCommand(params);
|
|
218
|
+
const content = execSync(command, { encoding: 'utf-8' });
|
|
219
|
+
const result = {
|
|
220
|
+
searchType: 'repos',
|
|
221
|
+
query: params.query || '',
|
|
222
|
+
results: content,
|
|
223
|
+
rawOutput: content,
|
|
224
|
+
};
|
|
225
|
+
return createSuccessResult(result);
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
return createErrorResult('Failed to search GitHub repositories', error);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
async function viewGitHubRepositoryInfo(params) {
|
|
233
|
+
const cacheKey = generateCacheKey('gh-repo-view', params);
|
|
234
|
+
return withCache(cacheKey, async () => {
|
|
235
|
+
try {
|
|
236
|
+
const owner = params.owner || '';
|
|
237
|
+
const command = `gh repo view ${owner}/${params.repo}`;
|
|
238
|
+
const content = execSync(command, { encoding: 'utf-8' });
|
|
239
|
+
const result = {
|
|
240
|
+
owner,
|
|
241
|
+
repo: params.repo,
|
|
242
|
+
repositoryInfo: content,
|
|
243
|
+
rawOutput: content,
|
|
244
|
+
};
|
|
245
|
+
return createSuccessResult(result);
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
return createErrorResult('Failed to view GitHub repository', error);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
async function getUserOrganizations(params) {
|
|
253
|
+
const cacheKey = generateCacheKey('gh-orgs', params);
|
|
254
|
+
return withCache(cacheKey, async () => {
|
|
255
|
+
try {
|
|
256
|
+
const limit = params.limit || 30;
|
|
257
|
+
const command = `gh org list --limit ${limit}`;
|
|
258
|
+
const output = execSync(command, {
|
|
259
|
+
encoding: 'utf8',
|
|
260
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
261
|
+
});
|
|
262
|
+
return {
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: 'text',
|
|
266
|
+
text: `GitHub Organizations for authenticated user:
|
|
267
|
+
|
|
268
|
+
${output}
|
|
269
|
+
|
|
270
|
+
IMPORTANT: Use any of these organization names as the 'owner' parameter in other search tools:
|
|
271
|
+
- search_github_code
|
|
272
|
+
- search_github_repos
|
|
273
|
+
- search_github_commits
|
|
274
|
+
- search_github_pull_requests
|
|
275
|
+
- fetch_github_file_content
|
|
276
|
+
- view_repository
|
|
277
|
+
|
|
278
|
+
Example: If you see a private repository in the list above, use the organization name as the 'owner' to filter results to that organization.
|
|
279
|
+
If the search for code or repositories by owner fails, try searching without an owner filter`,
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
isError: false,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
return {
|
|
287
|
+
content: [
|
|
288
|
+
{
|
|
289
|
+
type: 'text',
|
|
290
|
+
text: `Failed to get user organizations: ${error.message}
|
|
291
|
+
|
|
292
|
+
Make sure you are authenticated with GitHub CLI (gh auth login) and have access to organizations.`,
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
isError: true,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
async function fetchGitHubFileContent(params) {
|
|
301
|
+
const cacheKey = generateCacheKey('gh-file-content', params);
|
|
302
|
+
return withCache(cacheKey, async () => {
|
|
303
|
+
try {
|
|
304
|
+
const command = `gh api /repos/${params.owner}/${params.repo}/contents/${params.filePath}?ref=${params.branch} --jq .content | base64 -d`;
|
|
305
|
+
const content = execSync(command, { encoding: 'utf-8' });
|
|
306
|
+
return {
|
|
307
|
+
content: [
|
|
308
|
+
{
|
|
309
|
+
type: 'text',
|
|
310
|
+
text: content,
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
isError: false,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
return createErrorResult('Failed to retrieve file content', error);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async function npmView(packageName) {
|
|
322
|
+
const cacheKey = generateCacheKey('npm-view', { packageName });
|
|
323
|
+
return withCache(cacheKey, async () => {
|
|
324
|
+
try {
|
|
325
|
+
const { stdout } = await execAsync(`npm view ${packageName} repository.url repository.directory dependencies devdependencies peerDependencies version --json`);
|
|
326
|
+
const result = JSON.parse(stdout);
|
|
327
|
+
return createSuccessResult(result);
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
return createErrorResult('Failed to get npm repository information', error);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
async function npmSearch(args) {
|
|
335
|
+
const { query, json = true, searchlimit = 20 } = args;
|
|
336
|
+
let command = `npm search "${query}" --searchlimit=${searchlimit}`;
|
|
337
|
+
if (json) {
|
|
338
|
+
command += ' --json';
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
const { stdout, stderr } = await execAsync(command);
|
|
342
|
+
if (stderr) {
|
|
343
|
+
return {
|
|
344
|
+
content: [{ type: 'text', text: `Error searching NPM: ${stderr}` }],
|
|
345
|
+
isError: true,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
content: [{ type: 'text', text: stdout }],
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
return {
|
|
354
|
+
content: [
|
|
355
|
+
{
|
|
356
|
+
type: 'text',
|
|
357
|
+
text: `Failed to execute npm search: ${error.message}`,
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
isError: true,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async function viewRepositoryStructure(params) {
|
|
365
|
+
const cacheKey = generateCacheKey('gh-repo-structure', params);
|
|
366
|
+
return withCache(cacheKey, async () => {
|
|
367
|
+
const { owner, repo, branch, path: requestedPath = '' } = params;
|
|
368
|
+
const directoryListing = [];
|
|
369
|
+
let apiResponse = null;
|
|
370
|
+
let actualBranch = branch;
|
|
371
|
+
// Define branch fallback order
|
|
372
|
+
const branchFallbacks = [branch, 'main', 'master', 'develop', 'trunk'];
|
|
373
|
+
try {
|
|
374
|
+
// Construct the path segment
|
|
375
|
+
const pathSegment = requestedPath.startsWith('/')
|
|
376
|
+
? requestedPath.substring(1)
|
|
377
|
+
: requestedPath;
|
|
378
|
+
// Try each branch in the fallback order
|
|
379
|
+
let lastError = null;
|
|
380
|
+
let success = false;
|
|
381
|
+
for (const tryBranch of branchFallbacks) {
|
|
382
|
+
try {
|
|
383
|
+
const command = `gh api repos/${owner}/${repo}/contents/${pathSegment}?ref=${tryBranch}`;
|
|
384
|
+
const stdout = execSync(command, { encoding: 'utf-8' });
|
|
385
|
+
const items = JSON.parse(stdout);
|
|
386
|
+
// If we get here, the request succeeded
|
|
387
|
+
apiResponse = items;
|
|
388
|
+
actualBranch = tryBranch;
|
|
389
|
+
success = true;
|
|
390
|
+
// Populate directoryListing with names of items at the current path
|
|
391
|
+
if (Array.isArray(items)) {
|
|
392
|
+
for (const item of items) {
|
|
393
|
+
let itemName = item.name;
|
|
394
|
+
if (item.type === 'dir') {
|
|
395
|
+
itemName += '/';
|
|
396
|
+
}
|
|
397
|
+
directoryListing.push(itemName);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
else if (items) {
|
|
401
|
+
// Handle single file case (though unusual for structure view)
|
|
402
|
+
if (items.name) {
|
|
403
|
+
directoryListing.push(items.type === 'dir' ? `${items.name}/` : items.name);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
break; // Success, exit the loop
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
410
|
+
// If this is not a 404 error (branch not found), or if it's the last attempt, break
|
|
411
|
+
const errorMessage = lastError.message.toLowerCase();
|
|
412
|
+
if (!errorMessage.includes('no commit found') &&
|
|
413
|
+
!errorMessage.includes('404') &&
|
|
414
|
+
!errorMessage.includes('not found')) {
|
|
415
|
+
// This is a different kind of error (permissions, network, etc.), don't continue fallback
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
// Continue to next branch fallback
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (!success) {
|
|
423
|
+
// All branch attempts failed
|
|
424
|
+
const attemptedBranches = branchFallbacks.join(', ');
|
|
425
|
+
throw new Error(`Failed to access repository structure. Tried branches: ${attemptedBranches}. ` +
|
|
426
|
+
`Last error: ${lastError?.message || 'Unknown error'}`);
|
|
427
|
+
}
|
|
428
|
+
directoryListing.sort(); // Sort for consistent output
|
|
429
|
+
const result = {
|
|
430
|
+
owner,
|
|
431
|
+
repo,
|
|
432
|
+
branch: actualBranch, // Include the branch that actually worked
|
|
433
|
+
structure: directoryListing,
|
|
434
|
+
rawOutput: apiResponse,
|
|
435
|
+
// Add metadata about branch fallback if different from requested
|
|
436
|
+
...(actualBranch !== branch && {
|
|
437
|
+
branchFallback: {
|
|
438
|
+
requested: branch,
|
|
439
|
+
used: actualBranch,
|
|
440
|
+
message: `Branch '${branch}' not found, used '${actualBranch}' instead`,
|
|
441
|
+
},
|
|
442
|
+
}),
|
|
443
|
+
};
|
|
444
|
+
return createSuccessResult(result);
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
// Final error handling with comprehensive error message
|
|
448
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
449
|
+
return createErrorResult(`Failed to view GitHub repository structure for ${owner}/${repo} at path '${requestedPath}': ${errorMessage}`, error);
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
async function searchGitHubIssues(params) {
|
|
454
|
+
const cacheKey = generateCacheKey('gh-issues', params);
|
|
455
|
+
return withCache(cacheKey, async () => {
|
|
456
|
+
try {
|
|
457
|
+
const command = buildGitHubIssuesSearchCommand(params);
|
|
458
|
+
const content = execSync(command, { encoding: 'utf-8' });
|
|
459
|
+
const result = {
|
|
460
|
+
searchType: 'issues',
|
|
461
|
+
query: params.query || '',
|
|
462
|
+
results: content,
|
|
463
|
+
rawOutput: content,
|
|
464
|
+
};
|
|
465
|
+
return createSuccessResult(result);
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
return createErrorResult('Failed to search GitHub issues', error);
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
async function searchGitHubTopics(params) {
|
|
473
|
+
const cacheKey = generateCacheKey('gh-topics', params);
|
|
474
|
+
return withCache(cacheKey, async () => {
|
|
475
|
+
try {
|
|
476
|
+
// Use GitHub API for topics search since gh search topics doesn't exist
|
|
477
|
+
const command = buildGitHubTopicsAPICommand(params);
|
|
478
|
+
const content = execSync(command, { encoding: 'utf-8' });
|
|
479
|
+
const result = {
|
|
480
|
+
searchType: 'topics',
|
|
481
|
+
query: params.query || '',
|
|
482
|
+
results: content,
|
|
483
|
+
rawOutput: content,
|
|
484
|
+
};
|
|
485
|
+
return createSuccessResult(result);
|
|
486
|
+
}
|
|
487
|
+
catch (error) {
|
|
488
|
+
return createErrorResult('Failed to search GitHub topics', error);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
async function searchGitHubUsers(params) {
|
|
493
|
+
const cacheKey = generateCacheKey('gh-users', params);
|
|
494
|
+
return withCache(cacheKey, async () => {
|
|
495
|
+
try {
|
|
496
|
+
// Use GitHub API for users search since gh search users doesn't exist
|
|
497
|
+
const command = buildGitHubUsersAPICommand(params);
|
|
498
|
+
const content = execSync(command, { encoding: 'utf-8' });
|
|
499
|
+
const result = {
|
|
500
|
+
searchType: 'users',
|
|
501
|
+
query: params.query || '',
|
|
502
|
+
results: content,
|
|
503
|
+
rawOutput: content,
|
|
504
|
+
};
|
|
505
|
+
return createSuccessResult(result);
|
|
506
|
+
}
|
|
507
|
+
catch (error) {
|
|
508
|
+
return createErrorResult('Failed to search GitHub users', error);
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
async function searchGitHubDiscussions(params) {
|
|
513
|
+
const cacheKey = generateCacheKey('gh-discussions', params);
|
|
514
|
+
return withCache(cacheKey, async () => {
|
|
515
|
+
try {
|
|
516
|
+
// Use GitHub API for discussions search since gh search discussions doesn't exist
|
|
517
|
+
const command = buildGitHubDiscussionsAPICommand(params);
|
|
518
|
+
const content = execSync(command, { encoding: 'utf-8' });
|
|
519
|
+
const result = {
|
|
520
|
+
searchType: 'discussions',
|
|
521
|
+
query: params.query || '',
|
|
522
|
+
results: content,
|
|
523
|
+
rawOutput: content,
|
|
524
|
+
};
|
|
525
|
+
// Parse the response to check if we have any discussions
|
|
526
|
+
try {
|
|
527
|
+
const parsedContent = JSON.parse(content);
|
|
528
|
+
const discussionCount = parsedContent?.data?.search?.discussionCount || 0;
|
|
529
|
+
// If no discussions found and we have a specific owner, provide helpful context
|
|
530
|
+
if (discussionCount === 0 && params.owner) {
|
|
531
|
+
const scopeInfo = params.repo
|
|
532
|
+
? `repository "${params.owner}/${params.repo}"`
|
|
533
|
+
: `organization "${params.owner}"`;
|
|
534
|
+
result.results = JSON.stringify({
|
|
535
|
+
...parsedContent,
|
|
536
|
+
searchInfo: {
|
|
537
|
+
message: `No discussions found in ${scopeInfo}. This search was scoped to ${params.owner} only and did not search other organizations. The repository may not have discussions enabled.`,
|
|
538
|
+
searchScope: params.repo
|
|
539
|
+
? `repo:${params.owner}/${params.repo}`
|
|
540
|
+
: `org:${params.owner}`,
|
|
541
|
+
query: params.query,
|
|
542
|
+
discussionCount: 0,
|
|
543
|
+
},
|
|
544
|
+
}, null, 2);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
catch (parseError) {
|
|
548
|
+
// If we can't parse the JSON, just return the original result
|
|
549
|
+
}
|
|
550
|
+
return createSuccessResult(result);
|
|
551
|
+
}
|
|
552
|
+
catch (error) {
|
|
553
|
+
return createErrorResult('Failed to search GitHub discussions', error);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
function buildGitHubCodeSearchCommand(params) {
|
|
558
|
+
// Apply progressive single-word search strategy for optimal discovery
|
|
559
|
+
const processedQuery = processSearchQuery(params.query || '');
|
|
560
|
+
let command = `gh search code ${processedQuery}`;
|
|
561
|
+
if (params.owner)
|
|
562
|
+
command += ` --owner ${params.owner}`;
|
|
563
|
+
if (params.repo)
|
|
564
|
+
command += ` --repo ${params.repo}`;
|
|
565
|
+
if (params.language)
|
|
566
|
+
command += ` --language ${params.language}`;
|
|
567
|
+
if (params.filename)
|
|
568
|
+
command += ` --filename ${params.filename}`;
|
|
569
|
+
if (params.extension)
|
|
570
|
+
command += ` --extension ${params.extension}`;
|
|
571
|
+
if (params.match)
|
|
572
|
+
command += ` --match ${params.match}`;
|
|
573
|
+
if (params.limit)
|
|
574
|
+
command += ` --limit ${params.limit}`;
|
|
575
|
+
return command;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Process search query to implement progressive single-word search strategy:
|
|
579
|
+
* - Prioritize single terms over combined searches
|
|
580
|
+
* - "RAG" stays as "RAG" (single word search)
|
|
581
|
+
* - "RAG Ranking" becomes "RAG" (use first term only for initial search)
|
|
582
|
+
* - Only combine terms when explicitly needed after initial searches
|
|
583
|
+
* - Preserve quoted phrases as-is for exact matches
|
|
584
|
+
*/
|
|
585
|
+
function processSearchQuery(query) {
|
|
586
|
+
if (!query)
|
|
587
|
+
return '""';
|
|
588
|
+
// Check if query is already properly quoted single term
|
|
589
|
+
const singleQuotedPattern = /^"[^"]*"$/;
|
|
590
|
+
if (singleQuotedPattern.test(query.trim())) {
|
|
591
|
+
return query.trim();
|
|
592
|
+
}
|
|
593
|
+
// Check if query is multiple quoted terms (advanced search)
|
|
594
|
+
const multipleQuotedPattern = /^(\s*"[^"]+"\s*)+$/;
|
|
595
|
+
if (multipleQuotedPattern.test(query.trim())) {
|
|
596
|
+
return query.trim();
|
|
597
|
+
}
|
|
598
|
+
// Plain text query - PRIORITIZE SINGLE WORDS for progressive search
|
|
599
|
+
const terms = query
|
|
600
|
+
.trim()
|
|
601
|
+
.split(/\s+/)
|
|
602
|
+
.filter(term => term.length > 0);
|
|
603
|
+
if (terms.length === 0)
|
|
604
|
+
return '""';
|
|
605
|
+
// FOR PROGRESSIVE SEARCH: Use only the first term initially
|
|
606
|
+
// This implements the "RAG" then "Ranking" strategy instead of "RAG Ranking"
|
|
607
|
+
if (terms.length === 1) {
|
|
608
|
+
return `"${terms[0]}"`;
|
|
609
|
+
}
|
|
610
|
+
// For multiple terms, prioritize the first term (most important)
|
|
611
|
+
// This encourages users to do separate searches: "RAG" first, then "Ranking"
|
|
612
|
+
return `"${terms[0]}"`;
|
|
613
|
+
}
|
|
614
|
+
function buildGitHubCommitsSearchCommand(params) {
|
|
615
|
+
let command = `gh search commits "${params.query}"`;
|
|
616
|
+
if (params.owner)
|
|
617
|
+
command += ` --owner ${params.owner}`;
|
|
618
|
+
if (params.repo)
|
|
619
|
+
command += ` --repo ${params.repo}`;
|
|
620
|
+
if (params.author)
|
|
621
|
+
command += ` --author ${params.author}`;
|
|
622
|
+
if (params.committer)
|
|
623
|
+
command += ` --committer ${params.committer}`;
|
|
624
|
+
if (params.authorDate)
|
|
625
|
+
command += ` --author-date ${params.authorDate}`;
|
|
626
|
+
if (params.committerDate)
|
|
627
|
+
command += ` --committer-date ${params.committerDate}`;
|
|
628
|
+
if (params.authorEmail)
|
|
629
|
+
command += ` --author-email ${params.authorEmail}`;
|
|
630
|
+
if (params.authorName)
|
|
631
|
+
command += ` --author-name "${params.authorName}"`;
|
|
632
|
+
if (params.committerEmail)
|
|
633
|
+
command += ` --committer-email ${params.committerEmail}`;
|
|
634
|
+
if (params.committerName)
|
|
635
|
+
command += ` --committer-name "${params.committerName}"`;
|
|
636
|
+
if (params.merge !== undefined)
|
|
637
|
+
command += ` --merge`;
|
|
638
|
+
if (params.hash)
|
|
639
|
+
command += ` --hash ${params.hash}`;
|
|
640
|
+
if (params.parent)
|
|
641
|
+
command += ` --parent ${params.parent}`;
|
|
642
|
+
if (params.tree)
|
|
643
|
+
command += ` --tree ${params.tree}`;
|
|
644
|
+
if (params.visibility)
|
|
645
|
+
command += ` --visibility ${params.visibility}`;
|
|
646
|
+
if (params.limit)
|
|
647
|
+
command += ` --limit ${params.limit}`;
|
|
648
|
+
if (params.sort && params.sort !== 'best-match')
|
|
649
|
+
command += ` --sort ${params.sort}`;
|
|
650
|
+
if (params.order)
|
|
651
|
+
command += ` --order ${params.order}`;
|
|
652
|
+
return command;
|
|
653
|
+
}
|
|
654
|
+
function buildGitHubPullRequestsSearchCommand(params) {
|
|
655
|
+
let command = `gh search prs "${params.query}"`;
|
|
656
|
+
if (params.owner)
|
|
657
|
+
command += ` --owner ${params.owner}`;
|
|
658
|
+
if (params.repo)
|
|
659
|
+
command += ` --repo ${params.repo}`;
|
|
660
|
+
if (params.author)
|
|
661
|
+
command += ` --author ${params.author}`;
|
|
662
|
+
if (params.assignee)
|
|
663
|
+
command += ` --assignee ${params.assignee}`;
|
|
664
|
+
if (params.mentions)
|
|
665
|
+
command += ` --mentions ${params.mentions}`;
|
|
666
|
+
if (params.commenter)
|
|
667
|
+
command += ` --commenter ${params.commenter}`;
|
|
668
|
+
if (params.involves)
|
|
669
|
+
command += ` --involves ${params.involves}`;
|
|
670
|
+
if (params.reviewedBy)
|
|
671
|
+
command += ` --reviewed-by ${params.reviewedBy}`;
|
|
672
|
+
if (params.reviewRequested)
|
|
673
|
+
command += ` --review-requested ${params.reviewRequested}`;
|
|
674
|
+
if (params.state)
|
|
675
|
+
command += ` --state ${params.state}`;
|
|
676
|
+
if (params.head)
|
|
677
|
+
command += ` --head ${params.head}`;
|
|
678
|
+
if (params.base)
|
|
679
|
+
command += ` --base ${params.base}`;
|
|
680
|
+
if (params.language)
|
|
681
|
+
command += ` --language ${params.language}`;
|
|
682
|
+
if (params.created)
|
|
683
|
+
command += ` --created ${params.created}`;
|
|
684
|
+
if (params.updated)
|
|
685
|
+
command += ` --updated ${params.updated}`;
|
|
686
|
+
if (params.merged)
|
|
687
|
+
command += ` --merged ${params.merged}`;
|
|
688
|
+
if (params.closed)
|
|
689
|
+
command += ` --closed ${params.closed}`;
|
|
690
|
+
if (params.draft !== undefined)
|
|
691
|
+
command += ` --draft ${params.draft}`;
|
|
692
|
+
if (params.limit)
|
|
693
|
+
command += ` --limit ${params.limit}`;
|
|
694
|
+
if (params.sort)
|
|
695
|
+
command += ` --sort ${params.sort}`;
|
|
696
|
+
if (params.order)
|
|
697
|
+
command += ` --order ${params.order}`;
|
|
698
|
+
return command;
|
|
699
|
+
}
|
|
700
|
+
function buildGitHubReposSearchCommand(params) {
|
|
701
|
+
// Process query to use single-word strategy
|
|
702
|
+
const processedQuery = processSearchQuery(params.query || '');
|
|
703
|
+
let command = `gh search repos ${processedQuery}`;
|
|
704
|
+
if (params.owner)
|
|
705
|
+
command += ` --owner ${params.owner}`;
|
|
706
|
+
if (params.archived !== undefined)
|
|
707
|
+
command += ` --archived ${params.archived}`;
|
|
708
|
+
if (params.created)
|
|
709
|
+
command += ` --created "${params.created}"`;
|
|
710
|
+
if (params.followers !== undefined)
|
|
711
|
+
command += ` --followers ${params.followers}`;
|
|
712
|
+
if (params.forks !== undefined)
|
|
713
|
+
command += ` --forks ${params.forks}`;
|
|
714
|
+
if (params.goodFirstIssues !== undefined)
|
|
715
|
+
command += ` --good-first-issues ${params.goodFirstIssues}`;
|
|
716
|
+
if (params.helpWantedIssues !== undefined)
|
|
717
|
+
command += ` --help-wanted-issues ${params.helpWantedIssues}`;
|
|
718
|
+
if (params.includeForks)
|
|
719
|
+
command += ` --include-forks ${params.includeForks}`;
|
|
720
|
+
if (params.language)
|
|
721
|
+
command += ` --language ${params.language}`;
|
|
722
|
+
if (params.license)
|
|
723
|
+
command += ` --license ${params.license}`;
|
|
724
|
+
if (params.limit)
|
|
725
|
+
command += ` --limit ${params.limit}`;
|
|
726
|
+
if (params.match)
|
|
727
|
+
command += ` --match ${params.match}`;
|
|
728
|
+
if (params.numberTopics !== undefined)
|
|
729
|
+
command += ` --number-topics ${params.numberTopics}`;
|
|
730
|
+
if (params.order)
|
|
731
|
+
command += ` --order ${params.order}`;
|
|
732
|
+
if (params.size)
|
|
733
|
+
command += ` --size "${params.size}"`;
|
|
734
|
+
// DEFAULT TO UPDATED SORTING for recency prioritization
|
|
735
|
+
const sortBy = params.sort || 'updated';
|
|
736
|
+
if (sortBy !== 'best-match') {
|
|
737
|
+
command += ` --sort ${sortBy}`;
|
|
738
|
+
}
|
|
739
|
+
if (params.stars !== undefined)
|
|
740
|
+
command += ` --stars ${params.stars}`;
|
|
741
|
+
if (params.topic)
|
|
742
|
+
command += ` --topic ${params.topic}`;
|
|
743
|
+
if (params.updated)
|
|
744
|
+
command += ` --updated "${params.updated}"`;
|
|
745
|
+
if (params.visibility)
|
|
746
|
+
command += ` --visibility ${params.visibility}`;
|
|
747
|
+
return command;
|
|
748
|
+
}
|
|
749
|
+
function buildGitHubIssuesSearchCommand(params) {
|
|
750
|
+
let command = `gh search issues "${params.query}"`;
|
|
751
|
+
if (params.owner)
|
|
752
|
+
command += ` --owner ${params.owner}`;
|
|
753
|
+
if (params.repo)
|
|
754
|
+
command += ` --repo ${params.repo}`;
|
|
755
|
+
if (params.app)
|
|
756
|
+
command += ` --app ${params.app}`;
|
|
757
|
+
if (params.archived !== undefined)
|
|
758
|
+
command += ` --archived ${params.archived}`;
|
|
759
|
+
if (params.author)
|
|
760
|
+
command += ` --author ${params.author}`;
|
|
761
|
+
if (params.assignee)
|
|
762
|
+
command += ` --assignee ${params.assignee}`;
|
|
763
|
+
if (params.closed)
|
|
764
|
+
command += ` --closed ${params.closed}`;
|
|
765
|
+
if (params.commenter)
|
|
766
|
+
command += ` --commenter ${params.commenter}`;
|
|
767
|
+
if (params.comments !== undefined)
|
|
768
|
+
command += ` --comments ${params.comments}`;
|
|
769
|
+
if (params.created)
|
|
770
|
+
command += ` --created ${params.created}`;
|
|
771
|
+
if (params.includePrs !== undefined)
|
|
772
|
+
command += ` --include-prs`;
|
|
773
|
+
if (params.interactions !== undefined)
|
|
774
|
+
command += ` --interactions ${params.interactions}`;
|
|
775
|
+
if (params.involves)
|
|
776
|
+
command += ` --involves ${params.involves}`;
|
|
777
|
+
if (params.label)
|
|
778
|
+
command += ` --label ${params.label}`;
|
|
779
|
+
if (params.language)
|
|
780
|
+
command += ` --language ${params.language}`;
|
|
781
|
+
if (params.locked !== undefined)
|
|
782
|
+
command += ` --locked ${params.locked}`;
|
|
783
|
+
if (params.match)
|
|
784
|
+
command += ` --match ${params.match}`;
|
|
785
|
+
if (params.mentions)
|
|
786
|
+
command += ` --mentions ${params.mentions}`;
|
|
787
|
+
if (params.milestone)
|
|
788
|
+
command += ` --milestone ${params.milestone}`;
|
|
789
|
+
if (params.noAssignee !== undefined)
|
|
790
|
+
command += ` --no-assignee`;
|
|
791
|
+
if (params.noLabel !== undefined)
|
|
792
|
+
command += ` --no-label`;
|
|
793
|
+
if (params.noMilestone !== undefined)
|
|
794
|
+
command += ` --no-milestone`;
|
|
795
|
+
if (params.noProject !== undefined)
|
|
796
|
+
command += ` --no-project`;
|
|
797
|
+
if (params.project)
|
|
798
|
+
command += ` --project ${params.project}`;
|
|
799
|
+
if (params.reactions !== undefined)
|
|
800
|
+
command += ` --reactions ${params.reactions}`;
|
|
801
|
+
if (params.state)
|
|
802
|
+
command += ` --state ${params.state}`;
|
|
803
|
+
if (params.teamMentions)
|
|
804
|
+
command += ` --team-mentions ${params.teamMentions}`;
|
|
805
|
+
if (params.updated)
|
|
806
|
+
command += ` --updated ${params.updated}`;
|
|
807
|
+
if (params.visibility)
|
|
808
|
+
command += ` --visibility ${params.visibility}`;
|
|
809
|
+
if (params.limit)
|
|
810
|
+
command += ` --limit ${params.limit}`;
|
|
811
|
+
if (params.sort)
|
|
812
|
+
command += ` --sort ${params.sort}`;
|
|
813
|
+
if (params.order)
|
|
814
|
+
command += ` --order ${params.order}`;
|
|
815
|
+
return command;
|
|
816
|
+
}
|
|
817
|
+
function buildGitHubTopicsAPICommand(params) {
|
|
818
|
+
// Build GitHub API search query for topics
|
|
819
|
+
const searchQuery = params.query || '';
|
|
820
|
+
// Add filters to the search query
|
|
821
|
+
const queryParts = [searchQuery];
|
|
822
|
+
if (params.featured)
|
|
823
|
+
queryParts.push('is:featured');
|
|
824
|
+
if (params.curated)
|
|
825
|
+
queryParts.push('is:curated');
|
|
826
|
+
if (params.repositories)
|
|
827
|
+
queryParts.push(`repositories:${params.repositories}`);
|
|
828
|
+
if (params.created)
|
|
829
|
+
queryParts.push(`created:${params.created}`);
|
|
830
|
+
const finalQuery = queryParts.join(' ').trim();
|
|
831
|
+
// Use GitHub API to search topics
|
|
832
|
+
let command = `gh api search/topics -q '.items'`;
|
|
833
|
+
if (finalQuery) {
|
|
834
|
+
command = `gh api 'search/topics?q=${encodeURIComponent(finalQuery)}'`;
|
|
835
|
+
}
|
|
836
|
+
// Add pagination parameters
|
|
837
|
+
const limit = params.limit || 30;
|
|
838
|
+
command += `${finalQuery ? '&' : '?'}per_page=${limit}`;
|
|
839
|
+
if (params.sort)
|
|
840
|
+
command += `&sort=${params.sort}`;
|
|
841
|
+
if (params.order)
|
|
842
|
+
command += `&order=${params.order}`;
|
|
843
|
+
return command;
|
|
844
|
+
}
|
|
845
|
+
function buildGitHubUsersAPICommand(params) {
|
|
846
|
+
// Build GitHub API search query for users
|
|
847
|
+
const searchQuery = params.query || '';
|
|
848
|
+
// Add filters to the search query
|
|
849
|
+
const queryParts = [searchQuery];
|
|
850
|
+
if (params.type)
|
|
851
|
+
queryParts.push(`type:${params.type}`);
|
|
852
|
+
if (params.location)
|
|
853
|
+
queryParts.push(`location:"${params.location}"`);
|
|
854
|
+
if (params.language)
|
|
855
|
+
queryParts.push(`language:${params.language}`);
|
|
856
|
+
if (params.followers)
|
|
857
|
+
queryParts.push(`followers:${params.followers}`);
|
|
858
|
+
if (params.repos)
|
|
859
|
+
queryParts.push(`repos:${params.repos}`);
|
|
860
|
+
if (params.created)
|
|
861
|
+
queryParts.push(`created:${params.created}`);
|
|
862
|
+
const finalQuery = queryParts.join(' ').trim();
|
|
863
|
+
// Use GitHub API to search users
|
|
864
|
+
let command = `gh api search/users -q '.items'`;
|
|
865
|
+
if (finalQuery) {
|
|
866
|
+
command = `gh api 'search/users?q=${encodeURIComponent(finalQuery)}'`;
|
|
867
|
+
}
|
|
868
|
+
// Add pagination parameters
|
|
869
|
+
const limit = params.limit || 30;
|
|
870
|
+
command += `${finalQuery ? '&' : '?'}per_page=${limit}`;
|
|
871
|
+
if (params.sort)
|
|
872
|
+
command += `&sort=${params.sort}`;
|
|
873
|
+
if (params.order)
|
|
874
|
+
command += `&order=${params.order}`;
|
|
875
|
+
return command;
|
|
876
|
+
}
|
|
877
|
+
function buildGitHubDiscussionsAPICommand(params) {
|
|
878
|
+
// GitHub Discussions search is not available via REST API
|
|
879
|
+
// We'll use GraphQL API through gh api graphql
|
|
880
|
+
const query = `
|
|
881
|
+
query($searchQuery: String!, $first: Int!) {
|
|
882
|
+
search(query: $searchQuery, type: DISCUSSION, first: $first) {
|
|
883
|
+
discussionCount
|
|
884
|
+
edges {
|
|
885
|
+
node {
|
|
886
|
+
... on Discussion {
|
|
887
|
+
title
|
|
888
|
+
body
|
|
889
|
+
url
|
|
890
|
+
createdAt
|
|
891
|
+
updatedAt
|
|
892
|
+
author {
|
|
893
|
+
login
|
|
894
|
+
}
|
|
895
|
+
repository {
|
|
896
|
+
nameWithOwner
|
|
897
|
+
}
|
|
898
|
+
category {
|
|
899
|
+
name
|
|
900
|
+
}
|
|
901
|
+
answerChosenAt
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
`;
|
|
908
|
+
// Build search query
|
|
909
|
+
const searchQuery = params.query || '';
|
|
910
|
+
const queryParts = [searchQuery];
|
|
911
|
+
// Always scope to specific repo if both owner and repo are provided
|
|
912
|
+
if (params.repo && params.owner) {
|
|
913
|
+
queryParts.push(`repo:${params.owner}/${params.repo}`);
|
|
914
|
+
}
|
|
915
|
+
else if (params.owner) {
|
|
916
|
+
// If only owner is specified, search within that owner's organization
|
|
917
|
+
// This prevents fallback to user's organizations when searching for specific packages
|
|
918
|
+
queryParts.push(`org:${params.owner}`);
|
|
919
|
+
}
|
|
920
|
+
// If no owner is specified, search globally (current behavior)
|
|
921
|
+
if (params.author)
|
|
922
|
+
queryParts.push(`author:${params.author}`);
|
|
923
|
+
if (params.category)
|
|
924
|
+
queryParts.push(`category:"${params.category}"`);
|
|
925
|
+
if (params.answered !== undefined) {
|
|
926
|
+
queryParts.push(params.answered ? 'is:answered' : 'is:unanswered');
|
|
927
|
+
}
|
|
928
|
+
if (params.created)
|
|
929
|
+
queryParts.push(`created:${params.created}`);
|
|
930
|
+
if (params.updated)
|
|
931
|
+
queryParts.push(`updated:${params.updated}`);
|
|
932
|
+
const finalQuery = queryParts.join(' ').trim();
|
|
933
|
+
const limit = params.limit || 30;
|
|
934
|
+
return `gh api graphql -f query='${query}' -f searchQuery='${finalQuery}' -F first=${limit}`;
|
|
935
|
+
}
|
|
936
|
+
function createSuccessResult(data) {
|
|
937
|
+
return {
|
|
938
|
+
content: [
|
|
939
|
+
{
|
|
940
|
+
type: 'text',
|
|
941
|
+
text: JSON.stringify(data, null, 2),
|
|
942
|
+
},
|
|
943
|
+
],
|
|
944
|
+
isError: false,
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function createErrorResult(message, error) {
|
|
948
|
+
return {
|
|
949
|
+
content: [
|
|
950
|
+
{
|
|
951
|
+
type: 'text',
|
|
952
|
+
text: `${message}: ${error.message}`,
|
|
953
|
+
},
|
|
954
|
+
],
|
|
955
|
+
isError: true,
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
const SEARCH_GITHUB_CODE_DESCRIPTION = `Advanced code discovery engine for finding battle-tested implementations with intelligent pattern recognition.
|
|
960
|
+
|
|
961
|
+
**🧠 INTELLIGENT SEARCH STRATEGIES:**
|
|
962
|
+
|
|
963
|
+
**1. SEMANTIC CODE PATTERNS** (highest precision):
|
|
964
|
+
- **Function Signatures**: \`function handleAuth(\`, \`async function process\`
|
|
965
|
+
- **Class Definitions**: \`class AuthProvider\`, \`class DataManager\`
|
|
966
|
+
- **Import Patterns**: \`import { useAuth }\`, \`from '@/utils/auth'\`
|
|
967
|
+
- **Type Definitions**: \`interface UserType\`, \`type ApiResponse\`
|
|
968
|
+
|
|
969
|
+
**2. CONTEXTUAL IMPLEMENTATION SEARCH**:
|
|
970
|
+
- **Error Handling**: \`try { await\`, \`catch (error)\`, \`.catch(\`
|
|
971
|
+
- **State Management**: \`useState(\`, \`useEffect(\`, \`const [state\`
|
|
972
|
+
- **API Patterns**: \`fetch(\`, \`axios.post\`, \`api.get\`
|
|
973
|
+
- **Testing Patterns**: \`describe(\`, \`it('should\`, \`expect(\`
|
|
974
|
+
|
|
975
|
+
**3. ARCHITECTURAL DISCOVERY**:
|
|
976
|
+
- **Middleware**: \`app.use(\`, \`middleware(\`, \`next(\`
|
|
977
|
+
- **Routing**: \`router.get\`, \`Route path\`, \`useRouter\`
|
|
978
|
+
- **Database**: \`SELECT\`, \`INSERT INTO\`, \`mongoose.Schema\`
|
|
979
|
+
- **Configuration**: \`config.\`, \`process.env.\`, \`dotenv\`
|
|
980
|
+
|
|
981
|
+
**🎯 PROGRESSIVE SEARCH REFINEMENT:**
|
|
982
|
+
|
|
983
|
+
**Phase 1 - Broad Discovery**:
|
|
984
|
+
- Start with high-level concepts: "authentication", "validation", "caching"
|
|
985
|
+
- Use language filters for technology focus
|
|
986
|
+
- Target active repositories (>100 stars or recent commits)
|
|
987
|
+
|
|
988
|
+
**Phase 2 - Pattern Refinement**:
|
|
989
|
+
- Add implementation specifics: "JWT authentication", "form validation"
|
|
990
|
+
- Include framework context: "React authentication", "Express middleware"
|
|
991
|
+
- Filter by file extensions: .ts, .js, .py, .go
|
|
992
|
+
|
|
993
|
+
**Phase 3 - Precise Implementation**:
|
|
994
|
+
- Exact code constructs: "function validateJWT", "useAuth hook"
|
|
995
|
+
- Specific libraries: "passport.js", "next-auth", "auth0"
|
|
996
|
+
- Edge cases: "error handling", "refresh tokens"
|
|
997
|
+
|
|
998
|
+
**🔍 CONTEXT-AWARE SEARCH OPTIMIZATION:**
|
|
999
|
+
|
|
1000
|
+
**Query Classification**:
|
|
1001
|
+
- **Library Research**: Focus on popular implementations and examples
|
|
1002
|
+
- **Problem Solving**: Include error handling and edge cases
|
|
1003
|
+
- **Learning**: Prioritize well-documented, educational examples
|
|
1004
|
+
- **Architecture**: Emphasize complete systems and integrations
|
|
1005
|
+
|
|
1006
|
+
**Quality Indicators**:
|
|
1007
|
+
- **Production Signals**: Error handling, logging, monitoring
|
|
1008
|
+
- **Modern Patterns**: TypeScript usage, async/await, hooks
|
|
1009
|
+
- **Documentation**: Inline comments, JSDoc, README examples
|
|
1010
|
+
- **Testing**: Co-located test files and comprehensive coverage
|
|
1011
|
+
|
|
1012
|
+
**ADAPTIVE RESULT PROCESSING:**
|
|
1013
|
+
- **1-15 results**: Extract all via ${TOOL_NAMES.FETCH_GITHUB_FILE_CONTENT}
|
|
1014
|
+
- **16-50 results**: Quality filter by stars/activity → extract top 10
|
|
1015
|
+
- **51-200 results**: Add language/framework filters → re-search
|
|
1016
|
+
- **200+ results**: Use specific code constructs → targeted extraction
|
|
1017
|
+
|
|
1018
|
+
**🚀 ADVANCED SEARCH TECHNIQUES:**
|
|
1019
|
+
|
|
1020
|
+
**Multi-dimensional Search**:
|
|
1021
|
+
- **Horizontal**: Same pattern across languages/frameworks
|
|
1022
|
+
- **Vertical**: Deep dive into specific implementation approaches
|
|
1023
|
+
- **Temporal**: Track pattern evolution through commit history
|
|
1024
|
+
- **Organizational**: Compare approaches across teams/companies
|
|
1025
|
+
|
|
1026
|
+
**Cross-Reference Validation**:
|
|
1027
|
+
- **Issue Correlation**: Link implementations to solved problems
|
|
1028
|
+
- **PR Analysis**: Understand implementation decisions and trade-offs
|
|
1029
|
+
- **Discussion Context**: Community consensus and best practices
|
|
1030
|
+
- **Commit History**: Evolution and stability of patterns
|
|
1031
|
+
|
|
1032
|
+
**FAILURE RECOVERY STRATEGIES:**
|
|
1033
|
+
- **Semantic Expansion**: "auth" → "authentication", "authorization"
|
|
1034
|
+
- **Technology Translation**: "React auth" → "Vue authentication"
|
|
1035
|
+
- **Abstraction Levels**: "JWT implementation" → "token-based auth"
|
|
1036
|
+
- **Alternative Ecosystems**: Explore related technologies and patterns
|
|
1037
|
+
|
|
1038
|
+
**REQUIREMENTS:**
|
|
1039
|
+
- **MANDATORY**: Always use ${TOOL_NAMES.VIEW_REPOSITORY} first for branch discovery - **NEVER** search code without this step
|
|
1040
|
+
- Target repositories: >1K stars OR recent activity OR enterprise usage
|
|
1041
|
+
- Cross-validate findings with ${TOOL_NAMES.SEARCH_GITHUB_ISSUES} when needed
|
|
1042
|
+
- Extract complete context including imports, types, and documentation
|
|
1043
|
+
|
|
1044
|
+
**DO NOT:**
|
|
1045
|
+
- Search code without first calling ${TOOL_NAMES.VIEW_REPOSITORY} for branch discovery
|
|
1046
|
+
- Start with overly specific phrases before broad exploration
|
|
1047
|
+
- Skip cross-validation for critical implementation decisions
|
|
1048
|
+
- Extract code without understanding its architectural context
|
|
1049
|
+
- Ignore error handling and edge case implementations`;
|
|
1050
|
+
|
|
1051
|
+
function registerSearchGitHubCodeTool(server) {
|
|
1052
|
+
server.tool(TOOL_NAMES.SEARCH_GITHUB_CODE, SEARCH_GITHUB_CODE_DESCRIPTION, {
|
|
1053
|
+
query: z.string().describe('Search query for code'),
|
|
1054
|
+
owner: z.string().describe('Repository owner/organization'),
|
|
1055
|
+
repo: z
|
|
1056
|
+
.string()
|
|
1057
|
+
.optional()
|
|
1058
|
+
.describe('Repository name (often too restrictive)'),
|
|
1059
|
+
branch: z
|
|
1060
|
+
.string()
|
|
1061
|
+
.describe('Branch for workflow documentation (required but not used by CLI)'),
|
|
1062
|
+
language: z.string().optional().describe('Programming language filter'),
|
|
1063
|
+
filename: z.string().optional().describe('Filename filter'),
|
|
1064
|
+
extension: z.string().optional().describe('File extension filter'),
|
|
1065
|
+
match: z.enum(['file', 'path']).optional().describe('Search scope'),
|
|
1066
|
+
limit: z
|
|
1067
|
+
.number()
|
|
1068
|
+
.optional()
|
|
1069
|
+
.default(50)
|
|
1070
|
+
.describe('Maximum results (default: 50)'),
|
|
1071
|
+
}, async (args) => {
|
|
1072
|
+
try {
|
|
1073
|
+
return await searchGitHubCode(args);
|
|
1074
|
+
}
|
|
1075
|
+
catch (error) {
|
|
1076
|
+
return {
|
|
1077
|
+
content: [
|
|
1078
|
+
{
|
|
1079
|
+
type: 'text',
|
|
1080
|
+
text: `Failed to search GitHub code: ${error.message}`,
|
|
1081
|
+
},
|
|
1082
|
+
],
|
|
1083
|
+
isError: true,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
const FETCH_GITHUB_FILE_CONTENT_DESCRIPTION = `Extract complete working code with full context - the core of code analysis.
|
|
1090
|
+
|
|
1091
|
+
**PURPOSE:**
|
|
1092
|
+
Extract complete, working code implementations rather than code snippets.
|
|
1093
|
+
|
|
1094
|
+
**CRITICAL FOR SEARCH EXPANSION:**
|
|
1095
|
+
Transform search results into actionable implementations. Search tools find files - this tool extracts the complete, working code with full context needed for implementation.
|
|
1096
|
+
|
|
1097
|
+
**MANDATORY WORKFLOW:**
|
|
1098
|
+
1. **ALWAYS** Use ${TOOL_NAMES.VIEW_REPOSITORY} first to discover the correct default branch
|
|
1099
|
+
2. Find files with ${TOOL_NAMES.SEARCH_GITHUB_CODE}
|
|
1100
|
+
3. Extract complete implementations with this tool
|
|
1101
|
+
4. Follow dependency chains to related files
|
|
1102
|
+
|
|
1103
|
+
**CRITICAL REQUIREMENT:**
|
|
1104
|
+
**NEVER** use this tool without first calling ${TOOL_NAMES.VIEW_REPOSITORY} to get the correct branch information. Wrong branch names cause complete tool failure.
|
|
1105
|
+
|
|
1106
|
+
**WHAT TO FETCH:**
|
|
1107
|
+
- **Core implementations**: Main functions/classes with complete logic
|
|
1108
|
+
- **Dependencies**: Files referenced in imports/exports paths
|
|
1109
|
+
- **Configuration**: package.json, tsconfig.json, webpack.config.js, etc.
|
|
1110
|
+
- **Documentation**: README.md, API docs, usage examples
|
|
1111
|
+
- **Tests**: For usage patterns and validation
|
|
1112
|
+
- **Related utilities**: Helper functions and shared modules
|
|
1113
|
+
|
|
1114
|
+
**ENHANCED AUTO-RECOVERY:**
|
|
1115
|
+
Tries multiple fallback strategies:
|
|
1116
|
+
1. Specified branch → main → master → develop → trunk (with ref parameter)
|
|
1117
|
+
2. If all fail: Try without ref parameter (uses repository default branch)
|
|
1118
|
+
Handles incorrect branch info from ${TOOL_NAMES.VIEW_REPOSITORY} and GitHub API limitations.
|
|
1119
|
+
|
|
1120
|
+
**SUCCESS CRITERIA:**
|
|
1121
|
+
Production-ready code with all necessary context for immediate implementation.
|
|
1122
|
+
|
|
1123
|
+
**DO NOT:**
|
|
1124
|
+
- Extract incomplete snippets without context
|
|
1125
|
+
- Skip dependency files or configuration
|
|
1126
|
+
- Use this tool without first calling ${TOOL_NAMES.VIEW_REPOSITORY}
|
|
1127
|
+
- Assume branch names without verification`;
|
|
1128
|
+
|
|
1129
|
+
function registerFetchGitHubFileContentTool(server) {
|
|
1130
|
+
server.tool(TOOL_NAMES.FETCH_GITHUB_FILE_CONTENT, FETCH_GITHUB_FILE_CONTENT_DESCRIPTION, {
|
|
1131
|
+
owner: z
|
|
1132
|
+
.string()
|
|
1133
|
+
.describe(`Filter by repository owner/organization (e.g., 'example-org') get from ${TOOL_NAMES.GET_USER_ORGANIZATIONS} tool`),
|
|
1134
|
+
repo: z.string().describe('The name of the GitHub repository'),
|
|
1135
|
+
branch: z
|
|
1136
|
+
.string()
|
|
1137
|
+
.describe(`RECOMMENDED: The branch of the repository (e.g., "main", "master", "dev"). If branch doesn't exist, automatically tries common alternatives (main/master/develop/trunk) and finally default branch. Get from ${TOOL_NAMES.VIEW_REPOSITORY} for best results.`),
|
|
1138
|
+
filePath: z
|
|
1139
|
+
.string()
|
|
1140
|
+
.describe('The path to the file within the repository'),
|
|
1141
|
+
}, async (args) => {
|
|
1142
|
+
try {
|
|
1143
|
+
return await fetchGitHubFileContent(args);
|
|
1144
|
+
}
|
|
1145
|
+
catch (error) {
|
|
1146
|
+
return {
|
|
1147
|
+
content: [
|
|
1148
|
+
{
|
|
1149
|
+
type: 'text',
|
|
1150
|
+
text: `Failed to fetch GitHub file content: ${error.message}`,
|
|
1151
|
+
},
|
|
1152
|
+
],
|
|
1153
|
+
isError: true,
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
const VIEW_REPOSITORY_DESCRIPTION = `**CRITICAL: MANDATORY first step for all file operations** - discovers default branch information.
|
|
1160
|
+
|
|
1161
|
+
**PURPOSE:**
|
|
1162
|
+
Extract default branch from repository metadata and README to prevent tool failures. This tool provides the foundation for all subsequent GitHub operations.
|
|
1163
|
+
|
|
1164
|
+
**WHY ABSOLUTELY CRITICAL:**
|
|
1165
|
+
- Wrong branch names cause **COMPLETE TOOL FAILURE** in all file operations
|
|
1166
|
+
- Only correct branches contain current repository state
|
|
1167
|
+
- **ALL** subsequent GitHub operations depend on correct branch discovery
|
|
1168
|
+
- Prevents expensive retry cycles and API rate limiting
|
|
1169
|
+
|
|
1170
|
+
**BRANCH DISCOVERY METHODS:**
|
|
1171
|
+
- License badge URLs: github.com/OWNER/REPO/blob/BRANCH/LICENSE
|
|
1172
|
+
- CI/CD badge URLs: Check for ?branch=BRANCH parameters
|
|
1173
|
+
- Repository metadata: Default branch in CLI output
|
|
1174
|
+
- README badges and links analysis
|
|
1175
|
+
|
|
1176
|
+
**ABSOLUTELY REQUIRED BEFORE:**
|
|
1177
|
+
- ${TOOL_NAMES.SEARCH_GITHUB_CODE} (needs branch for workflow)
|
|
1178
|
+
- ${TOOL_NAMES.VIEW_REPOSITORY_STRUCTURE} (needs branch for directory exploration)
|
|
1179
|
+
- ${TOOL_NAMES.FETCH_GITHUB_FILE_CONTENT} (needs branch for file fetching)
|
|
1180
|
+
|
|
1181
|
+
**ENHANCED INTEGRATION:**
|
|
1182
|
+
Tools now include intelligent fallback mechanisms that complement this tool's branch discovery:
|
|
1183
|
+
- Multiple branch attempts with common alternatives
|
|
1184
|
+
- Graceful degradation when specific branches fail
|
|
1185
|
+
- Comprehensive error reporting for troubleshooting
|
|
1186
|
+
|
|
1187
|
+
**SUCCESS INDICATORS:**
|
|
1188
|
+
- Repository info retrieved successfully
|
|
1189
|
+
- Branch identifiable in badges/metadata
|
|
1190
|
+
- Repository appears active with recent commits
|
|
1191
|
+
- Default branch clearly indicated in output
|
|
1192
|
+
|
|
1193
|
+
**ERROR HANDLING:**
|
|
1194
|
+
- Not found: Check owner/repo spelling and permissions
|
|
1195
|
+
- Access denied: Use ${TOOL_NAMES.GET_USER_ORGANIZATIONS} for permissions
|
|
1196
|
+
- If this tool fails, all subsequent file operations will fail
|
|
1197
|
+
|
|
1198
|
+
**NEVER:**
|
|
1199
|
+
- Skip this step before any file operations
|
|
1200
|
+
- Assume default branch name without verification
|
|
1201
|
+
- Proceed with file operations if branch discovery fails
|
|
1202
|
+
- Use hardcoded branch names like 'main' or 'master' without verification`;
|
|
1203
|
+
|
|
1204
|
+
function registerViewRepositoryTool(server) {
|
|
1205
|
+
server.tool(TOOL_NAMES.VIEW_REPOSITORY, VIEW_REPOSITORY_DESCRIPTION, {
|
|
1206
|
+
owner: z
|
|
1207
|
+
.string()
|
|
1208
|
+
.describe("Filter by repository owner/organization (e.g., 'example-org')"),
|
|
1209
|
+
repo: z
|
|
1210
|
+
.string()
|
|
1211
|
+
.describe("The name of the GitHub repository to view (e.g. 'premium-ai-playground')"),
|
|
1212
|
+
}, async (args) => {
|
|
1213
|
+
try {
|
|
1214
|
+
return await viewGitHubRepositoryInfo(args);
|
|
1215
|
+
}
|
|
1216
|
+
catch (error) {
|
|
1217
|
+
return {
|
|
1218
|
+
content: [
|
|
1219
|
+
{
|
|
1220
|
+
type: 'text',
|
|
1221
|
+
text: `Failed to view repository: ${error.message}`,
|
|
1222
|
+
},
|
|
1223
|
+
],
|
|
1224
|
+
isError: true,
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
const NPM_VIEW_DESCRIPTION = `Transform package names into GitHub repositories for code analysis.
|
|
1231
|
+
|
|
1232
|
+
**WHEN TO USE:**
|
|
1233
|
+
- User mentions package names ("react", "lodash", "@types/node")
|
|
1234
|
+
- Code snippets with imports: import { useState } from 'react'
|
|
1235
|
+
- Dependency/module references in user queries
|
|
1236
|
+
|
|
1237
|
+
**WORKFLOW:**
|
|
1238
|
+
1. Extract repository URL from package.json
|
|
1239
|
+
2. Parse owner/repo from GitHub URL formats
|
|
1240
|
+
3. Chain to ${TOOL_NAMES.VIEW_REPOSITORY} for branch discovery
|
|
1241
|
+
4. Continue to ${TOOL_NAMES.SEARCH_GITHUB_CODE} for implementations
|
|
1242
|
+
|
|
1243
|
+
**OUTPUT:**
|
|
1244
|
+
Repository context + package metadata for intelligent code search
|
|
1245
|
+
|
|
1246
|
+
**EXAMPLES:**
|
|
1247
|
+
- "react" → github.com/facebook/react → Code search in React repo
|
|
1248
|
+
- "lodash" → github.com/lodash/lodash → Extract utility implementations
|
|
1249
|
+
- "@org/some-lib" → Private repo discovery → Organizational code analysis
|
|
1250
|
+
|
|
1251
|
+
**SUCCESS CRITERIA:**
|
|
1252
|
+
Accurate repository mapping that enables battle-tested code extraction
|
|
1253
|
+
|
|
1254
|
+
**DO NOT:**
|
|
1255
|
+
- Skip package-to-repository mapping when packages are mentioned
|
|
1256
|
+
- Assume package names without verification
|
|
1257
|
+
- Use for non-npm package references`;
|
|
1258
|
+
|
|
1259
|
+
function registerNpmViewTool(server) {
|
|
1260
|
+
server.tool(TOOL_NAMES.NPM_VIEW, NPM_VIEW_DESCRIPTION, {
|
|
1261
|
+
packageName: z
|
|
1262
|
+
.string()
|
|
1263
|
+
.describe("The name of the npm package to analyze (e.g., 'react', '@types/node', 'lodash')"),
|
|
1264
|
+
}, async (args) => {
|
|
1265
|
+
try {
|
|
1266
|
+
return await npmView(args.packageName);
|
|
1267
|
+
}
|
|
1268
|
+
catch (error) {
|
|
1269
|
+
return {
|
|
1270
|
+
content: [
|
|
1271
|
+
{
|
|
1272
|
+
type: 'text',
|
|
1273
|
+
text: `Failed to get npm package info: ${error.message}`,
|
|
1274
|
+
},
|
|
1275
|
+
],
|
|
1276
|
+
isError: true,
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
const SEARCH_GITHUB_REPOS_DESCRIPTION = `GitHub repository search with progressive discovery methodology.
|
|
1283
|
+
- **RELATED TOOLS:**
|
|
1284
|
+
- ${TOOL_NAMES.SEARCH_GITHUB_TOPICS}: Essential for topic-based search (e.g. "react", "typescript", "nodejs", "rag") and more discovery
|
|
1285
|
+
|
|
1286
|
+
**AUTO-TRIGGER CONDITIONS:**
|
|
1287
|
+
- Organization mentions: "I work at [Company Name]", "our team", "organization codebase"
|
|
1288
|
+
- Private repository indicators: "internal code", "team repositories"
|
|
1289
|
+
→ Auto-call ${TOOL_NAMES.GET_USER_ORGANIZATIONS}
|
|
1290
|
+
|
|
1291
|
+
**SEARCH STRATEGY:**
|
|
1292
|
+
1. **Start Ultra-Lean**: Single technical terms ("react", "cli", "docker")
|
|
1293
|
+
2. **Gradual Expansion**: Add second term only if first yields too many/few results
|
|
1294
|
+
3. **Specific Targeting**: Combine terms only when patterns emerge
|
|
1295
|
+
|
|
1296
|
+
**SEARCH METHODOLOGY:**
|
|
1297
|
+
- Phase 1: Single keywords ("graphql", "kubernetes", "typescript")
|
|
1298
|
+
- Phase 2: Add context if needed ("graphql server", "kubernetes operator")
|
|
1299
|
+
- Phase 3: Specific combinations only when necessary
|
|
1300
|
+
|
|
1301
|
+
**CONTEXT DETECTION:**
|
|
1302
|
+
- Organization context → Auto-call ${TOOL_NAMES.GET_USER_ORGANIZATIONS}
|
|
1303
|
+
- Package mentions → Use ${TOOL_NAMES.NPM_VIEW} first
|
|
1304
|
+
- General queries → Start without owner filter
|
|
1305
|
+
|
|
1306
|
+
**FILTERING STRATEGY:**
|
|
1307
|
+
- Owner filter: Most effective for scoping results
|
|
1308
|
+
- Language filter: For technology-specific searches
|
|
1309
|
+
- Stars filter: ">100" for established, ">10" for active projects
|
|
1310
|
+
- Updated filter: Prioritize recent activity (">2023-01-01")
|
|
1311
|
+
|
|
1312
|
+
**RESULT QUALITY TARGETS:**
|
|
1313
|
+
- 0 results: Broaden terms, remove filters except owner
|
|
1314
|
+
- 1-10: IDEAL - Deep dive analysis
|
|
1315
|
+
- 11-30: GOOD - Analyze patterns and select relevant
|
|
1316
|
+
- 31-100: Add specificity filters (language, stars, updated)
|
|
1317
|
+
- 100+: Too broad - use more specific single terms
|
|
1318
|
+
|
|
1319
|
+
**DEFAULT BEHAVIOR:**
|
|
1320
|
+
- Sort by "updated" for active repositories first
|
|
1321
|
+
- Balance relevance with recency
|
|
1322
|
+
|
|
1323
|
+
**DO NOT:**
|
|
1324
|
+
- Start with complex phrases like "react command line tools"
|
|
1325
|
+
- Use "react" when you mean single keyword search
|
|
1326
|
+
- Skip organization discovery when company context is clear`;
|
|
1327
|
+
|
|
1328
|
+
function registerSearchGitHubReposTool(server) {
|
|
1329
|
+
server.tool(TOOL_NAMES.SEARCH_GITHUB_REPOS, SEARCH_GITHUB_REPOS_DESCRIPTION, {
|
|
1330
|
+
query: z.string().describe('Search query for repositories'),
|
|
1331
|
+
owner: z.string().describe('Repository owner/organization'),
|
|
1332
|
+
archived: z.boolean().optional().describe('Filter archived state'),
|
|
1333
|
+
created: z.string().optional().describe('Filter by created date'),
|
|
1334
|
+
followers: z.number().optional().describe('Filter by followers count'),
|
|
1335
|
+
forks: z.number().optional().describe('Filter by forks count'),
|
|
1336
|
+
goodFirstIssues: z
|
|
1337
|
+
.number()
|
|
1338
|
+
.optional()
|
|
1339
|
+
.describe('Filter by good first issues count'),
|
|
1340
|
+
helpWantedIssues: z
|
|
1341
|
+
.number()
|
|
1342
|
+
.optional()
|
|
1343
|
+
.describe('Filter by help wanted issues count'),
|
|
1344
|
+
includeForks: z
|
|
1345
|
+
.enum(['false', 'true', 'only'])
|
|
1346
|
+
.optional()
|
|
1347
|
+
.describe('Include forks in results'),
|
|
1348
|
+
language: z
|
|
1349
|
+
.string()
|
|
1350
|
+
.optional()
|
|
1351
|
+
.describe('Filter by programming language'),
|
|
1352
|
+
license: z.string().optional().describe('Filter by license type'),
|
|
1353
|
+
limit: z
|
|
1354
|
+
.number()
|
|
1355
|
+
.optional()
|
|
1356
|
+
.default(50)
|
|
1357
|
+
.describe('Maximum results (default: 50)'),
|
|
1358
|
+
match: z
|
|
1359
|
+
.enum(['name', 'description', 'readme'])
|
|
1360
|
+
.optional()
|
|
1361
|
+
.describe('Search scope restriction'),
|
|
1362
|
+
numberTopics: z.number().optional().describe('Filter by topics count'),
|
|
1363
|
+
order: z
|
|
1364
|
+
.enum(['asc', 'desc'])
|
|
1365
|
+
.optional()
|
|
1366
|
+
.default('desc')
|
|
1367
|
+
.describe('Result order (default: desc for newest first)'),
|
|
1368
|
+
size: z.string().optional().describe('Filter by size in KB'),
|
|
1369
|
+
sort: z
|
|
1370
|
+
.enum(['forks', 'help-wanted-issues', 'stars', 'updated', 'best-match'])
|
|
1371
|
+
.optional()
|
|
1372
|
+
.default('updated')
|
|
1373
|
+
.describe('Sort criteria (default: updated for recent activity)'),
|
|
1374
|
+
stars: z.number().optional().describe('Filter by stars count'),
|
|
1375
|
+
topic: z.string().optional().describe('Filter by topic/tag'),
|
|
1376
|
+
updated: z.string().optional().describe('Filter by last update date'),
|
|
1377
|
+
visibility: z
|
|
1378
|
+
.enum(['public', 'private', 'internal'])
|
|
1379
|
+
.optional()
|
|
1380
|
+
.describe('Filter by visibility'),
|
|
1381
|
+
}, async (args) => {
|
|
1382
|
+
try {
|
|
1383
|
+
return await searchGitHubRepos(args);
|
|
1384
|
+
}
|
|
1385
|
+
catch (error) {
|
|
1386
|
+
return {
|
|
1387
|
+
content: [
|
|
1388
|
+
{
|
|
1389
|
+
type: 'text',
|
|
1390
|
+
text: `Failed to search GitHub repositories: ${error.message}`,
|
|
1391
|
+
},
|
|
1392
|
+
],
|
|
1393
|
+
isError: true,
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
const SEARCH_GITHUB_COMMITS_DESCRIPTION = `Advanced GitHub commits search for development history analysis.
|
|
1400
|
+
|
|
1401
|
+
**CORE PURPOSE:**
|
|
1402
|
+
Track code evolution, debug issues, analyze contributor patterns, and understand development history.
|
|
1403
|
+
|
|
1404
|
+
**SEARCH STRATEGY:**
|
|
1405
|
+
1. **Start Minimal**: Single keywords ("fix", "feature", "update") + owner/repo context
|
|
1406
|
+
2. **Progressive Expansion**: Add specific terms based on findings
|
|
1407
|
+
3. **Apply Filters**: Author, date ranges only when patterns emerge
|
|
1408
|
+
|
|
1409
|
+
**SEARCH PATTERNS:**
|
|
1410
|
+
- **General exploration**: "fix" or "update" with owner/repo → activity overview
|
|
1411
|
+
- **Feature tracking**: Single feature keyword → expand with related terms
|
|
1412
|
+
- **Bug investigation**: "bug" or "fix" → narrow by time/author
|
|
1413
|
+
- **Attribution**: Keywords + author filter → contribution analysis
|
|
1414
|
+
|
|
1415
|
+
**RESULT TARGETS:**
|
|
1416
|
+
- 0-5 results: Try broader terms, remove filters
|
|
1417
|
+
- 6-50 results: OPTIMAL - Extract insights
|
|
1418
|
+
- 51+ results: Add specific filters or narrow time ranges
|
|
1419
|
+
|
|
1420
|
+
**CRITICAL LIMITATIONS:**
|
|
1421
|
+
- Large organizations may return org-wide results instead of repo-specific
|
|
1422
|
+
- If irrelevant results: Switch to alternative approaches (changelog files, PR search)
|
|
1423
|
+
|
|
1424
|
+
**FALLBACK STRATEGY:**
|
|
1425
|
+
1. Commit search with minimal keywords
|
|
1426
|
+
2. Pull request searches for features/changes
|
|
1427
|
+
3. Fetch changelog files (CHANGELOG.md, RELEASES.md)
|
|
1428
|
+
4. Repository structure exploration
|
|
1429
|
+
|
|
1430
|
+
**BRANCH AWARENESS:** Verify default branch (main vs master) before file fetching
|
|
1431
|
+
|
|
1432
|
+
**ERROR HANDLING:**
|
|
1433
|
+
- "Search text required" → Use minimal keywords ("fix", "update")
|
|
1434
|
+
- Irrelevant results → Switch to file-based approaches
|
|
1435
|
+
- Empty results → Broaden scope or remove repository filters
|
|
1436
|
+
|
|
1437
|
+
**INTEGRATION:** Combine with ${TOOL_NAMES.FETCH_GITHUB_FILE_CONTENT} for complete change context.`;
|
|
1438
|
+
|
|
1439
|
+
function registerSearchGitHubCommitsTool(server) {
|
|
1440
|
+
server.tool(TOOL_NAMES.SEARCH_GITHUB_COMMITS, SEARCH_GITHUB_COMMITS_DESCRIPTION, {
|
|
1441
|
+
query: z
|
|
1442
|
+
.string()
|
|
1443
|
+
.describe("The search query to find commits - start with single keyword (e.g., 'bug', 'refactor', 'feature'). Cannot be empty as GitHub requires search text for commit searches."),
|
|
1444
|
+
owner: z
|
|
1445
|
+
.string()
|
|
1446
|
+
.optional()
|
|
1447
|
+
.describe("Filter by repository owner/organization (e.g., 'example-org') obtained from the appropriate tool for fetching user organizations"),
|
|
1448
|
+
repo: z
|
|
1449
|
+
.string()
|
|
1450
|
+
.optional()
|
|
1451
|
+
.describe("Filter by repository name (e.g., 'cli/cli')"),
|
|
1452
|
+
author: z.string().optional().describe('Filter by commit author'),
|
|
1453
|
+
committer: z.string().optional().describe('Filter by committer'),
|
|
1454
|
+
authorDate: z
|
|
1455
|
+
.string()
|
|
1456
|
+
.optional()
|
|
1457
|
+
.describe("Filter based on authored date (e.g., '>2022-01-01', '<2023-12-31')"),
|
|
1458
|
+
committerDate: z
|
|
1459
|
+
.string()
|
|
1460
|
+
.optional()
|
|
1461
|
+
.describe("Filter based on committed date (e.g., '>2022-01-01', '<2023-12-31')"),
|
|
1462
|
+
authorEmail: z.string().optional().describe('Filter on author email'),
|
|
1463
|
+
authorName: z.string().optional().describe('Filter on author name'),
|
|
1464
|
+
committerEmail: z
|
|
1465
|
+
.string()
|
|
1466
|
+
.optional()
|
|
1467
|
+
.describe('Filter on committer email'),
|
|
1468
|
+
committerName: z.string().optional().describe('Filter on committer name'),
|
|
1469
|
+
merge: z.boolean().optional().describe('Filter on merge commits'),
|
|
1470
|
+
hash: z.string().optional().describe('Filter by commit hash'),
|
|
1471
|
+
parent: z.string().optional().describe('Filter by parent hash'),
|
|
1472
|
+
tree: z.string().optional().describe('Filter by tree hash'),
|
|
1473
|
+
visibility: z
|
|
1474
|
+
.enum(['public', 'private', 'internal'])
|
|
1475
|
+
.optional()
|
|
1476
|
+
.describe('Filter based on repository visibility'),
|
|
1477
|
+
limit: z
|
|
1478
|
+
.number()
|
|
1479
|
+
.optional()
|
|
1480
|
+
.default(50)
|
|
1481
|
+
.describe('Maximum number of commits to return (default: 50)'),
|
|
1482
|
+
sort: z
|
|
1483
|
+
.enum(['author-date', 'committer-date', 'best-match'])
|
|
1484
|
+
.optional()
|
|
1485
|
+
.default('best-match')
|
|
1486
|
+
.describe('Sort commits by specified criteria (default: best-match)'),
|
|
1487
|
+
order: z
|
|
1488
|
+
.enum(['asc', 'desc'])
|
|
1489
|
+
.optional()
|
|
1490
|
+
.default('desc')
|
|
1491
|
+
.describe('Order of commits returned (default: desc for newest first)'),
|
|
1492
|
+
}, async (args) => {
|
|
1493
|
+
try {
|
|
1494
|
+
return await searchGitHubCommits(args);
|
|
1495
|
+
}
|
|
1496
|
+
catch (error) {
|
|
1497
|
+
return {
|
|
1498
|
+
content: [
|
|
1499
|
+
{
|
|
1500
|
+
type: 'text',
|
|
1501
|
+
text: `Failed to search GitHub commits: ${error.message}`,
|
|
1502
|
+
},
|
|
1503
|
+
],
|
|
1504
|
+
isError: true,
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
const SEARCH_GITHUB_PULL_REQUESTS_DESCRIPTION = `Advanced GitHub pull requests search for code review and feature analysis.
|
|
1511
|
+
|
|
1512
|
+
**SEARCH STRATEGY:**
|
|
1513
|
+
1. **Single Keywords First**: "bug", "feature", "refactor"
|
|
1514
|
+
2. **Then Combine**: "bug fix", "feature implementation" (only if needed)
|
|
1515
|
+
3. **Never Start Complex**: Avoid phrases like "comprehensive bug fix implementation"
|
|
1516
|
+
|
|
1517
|
+
**CORE PURPOSE:**
|
|
1518
|
+
- Code review insights and team collaboration patterns
|
|
1519
|
+
- Feature implementation lifecycle tracking
|
|
1520
|
+
- Breaking changes and quality assurance analysis
|
|
1521
|
+
|
|
1522
|
+
**SEARCH METHODOLOGY:**
|
|
1523
|
+
- **Phase 1**: Core discovery with fundamental terms ("authentication", "error")
|
|
1524
|
+
- **Phase 2**: Add context ("authentication JWT", "error handling")
|
|
1525
|
+
- **Phase 3**: Solution focus ("authentication bug fixed")
|
|
1526
|
+
|
|
1527
|
+
**RESULT TARGETS:**
|
|
1528
|
+
- 0 results: Try broader terms, remove filters
|
|
1529
|
+
- 1-20 results: IDEAL - Deep analysis of patterns and solutions
|
|
1530
|
+
- 21-100 results: GOOD - Add specificity or filters
|
|
1531
|
+
- 100+ results: Add specific terms or state/reviewer filters
|
|
1532
|
+
|
|
1533
|
+
**FILTERING BEST PRACTICES:**
|
|
1534
|
+
- State filter: "open" for current issues, "closed" for resolved patterns
|
|
1535
|
+
- Author/reviewer filters: Understand team collaboration
|
|
1536
|
+
- Draft filter: Work-in-progress vs completed features
|
|
1537
|
+
- Branch filters: Release and feature branch workflows
|
|
1538
|
+
- Language filter: Focus on specific technology stacks
|
|
1539
|
+
|
|
1540
|
+
**ADAPTIVE TACTICS:**
|
|
1541
|
+
- Start broad with feature keywords, narrow based on findings
|
|
1542
|
+
- Use owner/repo parameters when repository context is known
|
|
1543
|
+
- Widen scope if targeted searches yield insufficient results
|
|
1544
|
+
- Apply precise filters only after broader searches confirm patterns
|
|
1545
|
+
|
|
1546
|
+
**CROSS-REFERENCE STRATEGY:**
|
|
1547
|
+
- Combine with ${TOOL_NAMES.SEARCH_GITHUB_COMMITS} for complete development understanding
|
|
1548
|
+
- Use with ${TOOL_NAMES.SEARCH_GITHUB_CODE} for current implementations
|
|
1549
|
+
- Cross-reference with ${TOOL_NAMES.SEARCH_GITHUB_REPOS} for similar patterns
|
|
1550
|
+
|
|
1551
|
+
**QUALITY FOCUS:** Use review-related filters to find thoroughly vetted code examples.`;
|
|
1552
|
+
|
|
1553
|
+
function registerSearchGitHubPullRequestsTool(server) {
|
|
1554
|
+
server.tool(TOOL_NAMES.SEARCH_GITHUB_PULL_REQUESTS, SEARCH_GITHUB_PULL_REQUESTS_DESCRIPTION, {
|
|
1555
|
+
query: z
|
|
1556
|
+
.string()
|
|
1557
|
+
.describe("The search query to find pull requests (e.g., 'bug fix', 'feature implementation', 'code review')"),
|
|
1558
|
+
owner: z
|
|
1559
|
+
.string()
|
|
1560
|
+
.optional()
|
|
1561
|
+
.describe(`Filter by repository owner/organization (e.g., 'example-org')`),
|
|
1562
|
+
repo: z
|
|
1563
|
+
.string()
|
|
1564
|
+
.optional()
|
|
1565
|
+
.describe("Filter by repository name (e.g., 'cli/cli')"),
|
|
1566
|
+
author: z.string().optional().describe('Filter by pull request author'),
|
|
1567
|
+
assignee: z.string().optional().describe('Filter by assignee'),
|
|
1568
|
+
mentions: z.string().optional().describe('Filter based on user mentions'),
|
|
1569
|
+
commenter: z
|
|
1570
|
+
.string()
|
|
1571
|
+
.optional()
|
|
1572
|
+
.describe('Filter based on comments by user'),
|
|
1573
|
+
involves: z
|
|
1574
|
+
.string()
|
|
1575
|
+
.optional()
|
|
1576
|
+
.describe('Filter based on involvement of user'),
|
|
1577
|
+
reviewedBy: z.string().optional().describe('Filter on user who reviewed'),
|
|
1578
|
+
reviewRequested: z
|
|
1579
|
+
.string()
|
|
1580
|
+
.optional()
|
|
1581
|
+
.describe('Filter on user or team requested to review'),
|
|
1582
|
+
state: z
|
|
1583
|
+
.enum(['open', 'closed', 'merged'])
|
|
1584
|
+
.optional()
|
|
1585
|
+
.describe('Filter based on state'),
|
|
1586
|
+
head: z.string().optional().describe('Filter on head branch name'),
|
|
1587
|
+
base: z.string().optional().describe('Filter on base branch name'),
|
|
1588
|
+
language: z
|
|
1589
|
+
.string()
|
|
1590
|
+
.optional()
|
|
1591
|
+
.describe('Filter based on the coding language'),
|
|
1592
|
+
created: z
|
|
1593
|
+
.string()
|
|
1594
|
+
.optional()
|
|
1595
|
+
.describe("Filter based on created at date (e.g., '>2022-01-01', '<2023-12-31')"),
|
|
1596
|
+
updated: z.string().optional().describe('Filter on last updated at date'),
|
|
1597
|
+
merged: z.string().optional().describe('Filter on merged at date'),
|
|
1598
|
+
closed: z.string().optional().describe('Filter on closed at date'),
|
|
1599
|
+
draft: z.boolean().optional().describe('Filter based on draft state'),
|
|
1600
|
+
limit: z
|
|
1601
|
+
.number()
|
|
1602
|
+
.optional()
|
|
1603
|
+
.default(50)
|
|
1604
|
+
.describe('Maximum number of pull requests to return (default: 50)'),
|
|
1605
|
+
sort: z
|
|
1606
|
+
.enum([
|
|
1607
|
+
'comments',
|
|
1608
|
+
'reactions',
|
|
1609
|
+
'reactions-+1',
|
|
1610
|
+
'reactions--1',
|
|
1611
|
+
'reactions-smile',
|
|
1612
|
+
'reactions-thinking_face',
|
|
1613
|
+
'reactions-heart',
|
|
1614
|
+
'reactions-tada',
|
|
1615
|
+
'interactions',
|
|
1616
|
+
'created',
|
|
1617
|
+
'updated',
|
|
1618
|
+
])
|
|
1619
|
+
.optional()
|
|
1620
|
+
.describe('Sort pull requests by specified criteria'),
|
|
1621
|
+
order: z
|
|
1622
|
+
.enum(['asc', 'desc'])
|
|
1623
|
+
.optional()
|
|
1624
|
+
.default('desc')
|
|
1625
|
+
.describe('Order of results returned (default: desc)'),
|
|
1626
|
+
}, async (args) => {
|
|
1627
|
+
try {
|
|
1628
|
+
return await searchGitHubPullRequests(args);
|
|
1629
|
+
}
|
|
1630
|
+
catch (error) {
|
|
1631
|
+
return {
|
|
1632
|
+
content: [
|
|
1633
|
+
{
|
|
1634
|
+
type: 'text',
|
|
1635
|
+
text: `Failed to search GitHub pull requests: ${error.message}`,
|
|
1636
|
+
},
|
|
1637
|
+
],
|
|
1638
|
+
isError: true,
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
const GET_USER_ORGANIZATIONS_DESCRIPTION = `Get list of GitHub organizations for the authenticated user to enable private repository discovery.
|
|
1645
|
+
|
|
1646
|
+
**AUTOMATIC TRIGGERS:**
|
|
1647
|
+
Auto-trigger when users mention:
|
|
1648
|
+
- Company/employer context: "I work at [Company Name]", "our team", "company codebase"
|
|
1649
|
+
- Private repositories: "internal code", "team repositories"
|
|
1650
|
+
- Enterprise context: "at work", "enterprise setup"
|
|
1651
|
+
|
|
1652
|
+
**ORGANIZATION MATCHING Examples:**
|
|
1653
|
+
- "Facebook" → "facebook", "meta"
|
|
1654
|
+
- "Google" → "google", "googlecloudplatform"
|
|
1655
|
+
- "Microsoft" → "microsoft", "azure"
|
|
1656
|
+
|
|
1657
|
+
**WORKFLOW:**
|
|
1658
|
+
1. Call this tool first when organizational context detected
|
|
1659
|
+
2. Match user's company to found organizations
|
|
1660
|
+
3. Use selected organization as 'owner' parameter in subsequent searches
|
|
1661
|
+
4. Fallback to public search if no organizational results
|
|
1662
|
+
|
|
1663
|
+
**USAGE PRIORITY:**
|
|
1664
|
+
- Essential for private repository access
|
|
1665
|
+
- Critical for enterprise GitHub setups
|
|
1666
|
+
- Use before repository searches fail due to permissions
|
|
1667
|
+
- Combine with npmView for private organization packages
|
|
1668
|
+
|
|
1669
|
+
**DO NOT:**
|
|
1670
|
+
- Skip organization-scoped search when company context is clear
|
|
1671
|
+
- Use public search first when private repositories are likely
|
|
1672
|
+
- Ignore organizational context in user queries`;
|
|
1673
|
+
|
|
1674
|
+
function registerGetUserOrganizationsTool(server) {
|
|
1675
|
+
server.tool(TOOL_NAMES.GET_USER_ORGANIZATIONS, GET_USER_ORGANIZATIONS_DESCRIPTION, {
|
|
1676
|
+
limit: z
|
|
1677
|
+
.number()
|
|
1678
|
+
.optional()
|
|
1679
|
+
.default(50)
|
|
1680
|
+
.describe('Maximum number of organizations to list (default: 50)'),
|
|
1681
|
+
}, async (args) => {
|
|
1682
|
+
try {
|
|
1683
|
+
return await getUserOrganizations(args);
|
|
1684
|
+
}
|
|
1685
|
+
catch (error) {
|
|
1686
|
+
return {
|
|
1687
|
+
content: [
|
|
1688
|
+
{
|
|
1689
|
+
type: 'text',
|
|
1690
|
+
text: `Failed to get user organizations: ${error.message}`,
|
|
1691
|
+
},
|
|
1692
|
+
],
|
|
1693
|
+
isError: true,
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
const NPM_SEARCH_DESCRIPTION = `Search NPM registry for packages by keywords using "npm search <term>".
|
|
1700
|
+
|
|
1701
|
+
**SEARCH STRATEGY:**
|
|
1702
|
+
1. Start with single technology terms ("react", "cli")
|
|
1703
|
+
2. Add specificity only if needed ("react-hooks", "typescript-cli")
|
|
1704
|
+
3. Avoid complex phrases - they yield zero results
|
|
1705
|
+
|
|
1706
|
+
**WHEN TO USE:**
|
|
1707
|
+
- Pure discovery of packages on NPM registry by keyword
|
|
1708
|
+
- More direct than discovering packages mentioned in code
|
|
1709
|
+
- Finding alternatives to known packages
|
|
1710
|
+
|
|
1711
|
+
**SEARCH PATTERNS:**
|
|
1712
|
+
- Good: "react" → "cli" → "react cli" (if specific combination needed)
|
|
1713
|
+
- Poor: "react command line interface tools" (too complex, likely zero results)
|
|
1714
|
+
|
|
1715
|
+
**RESULT OPTIMIZATION:**
|
|
1716
|
+
- 0 results: Try broader, single-word terms
|
|
1717
|
+
- 1-20 results: IDEAL - analyze thoroughly
|
|
1718
|
+
- 21-100 results: GOOD - filter by popularity/relevance
|
|
1719
|
+
- 100+ results: Too broad - use more specific single terms
|
|
1720
|
+
|
|
1721
|
+
**OUTPUT FORMAT:**
|
|
1722
|
+
JSON format with package metadata including name, description, version, and popularity metrics
|
|
1723
|
+
|
|
1724
|
+
**CONSTRAINTS:**
|
|
1725
|
+
- Maximum 50 results by default
|
|
1726
|
+
- Supports regex patterns when query starts with /
|
|
1727
|
+
- Multiple space-separated terms supported but use sparingly`;
|
|
1728
|
+
|
|
1729
|
+
function registerNpmSearchTool(server) {
|
|
1730
|
+
server.tool(TOOL_NAMES.NPM_SEARCH, NPM_SEARCH_DESCRIPTION, {
|
|
1731
|
+
query: z
|
|
1732
|
+
.string()
|
|
1733
|
+
.describe("The search term(s) to find packages on NPM. Multiple terms can be space-separated (e.g., 'react query client'). Supports regex if term starts with /."),
|
|
1734
|
+
json: z
|
|
1735
|
+
.boolean()
|
|
1736
|
+
.optional()
|
|
1737
|
+
.default(true)
|
|
1738
|
+
.describe('Output search results in JSON format. Defaults to true.'),
|
|
1739
|
+
searchlimit: z
|
|
1740
|
+
.number()
|
|
1741
|
+
.optional()
|
|
1742
|
+
.default(50)
|
|
1743
|
+
.describe('Maximum number of search results to return. Defaults to 50.'),
|
|
1744
|
+
}, async (args) => {
|
|
1745
|
+
try {
|
|
1746
|
+
return await npmSearch(args);
|
|
1747
|
+
}
|
|
1748
|
+
catch (error) {
|
|
1749
|
+
return {
|
|
1750
|
+
content: [
|
|
1751
|
+
{
|
|
1752
|
+
type: 'text',
|
|
1753
|
+
text: `Failed to search NPM: ${error.message}`,
|
|
1754
|
+
},
|
|
1755
|
+
],
|
|
1756
|
+
isError: true,
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
const VIEW_REPOSITORY_STRUCTURE_DESCRIPTION = `Strategic repository exploration for code analysis.
|
|
1763
|
+
|
|
1764
|
+
**CRITICAL REQUIREMENT:**
|
|
1765
|
+
**MANDATORY:** Must use ${TOOL_NAMES.VIEW_REPOSITORY} FIRST to get branch. **NEVER** explore repository structure without explicit branch discovery.
|
|
1766
|
+
|
|
1767
|
+
**EXPLORATION PHASES:**
|
|
1768
|
+
1. **Root Analysis**: Project type (package.json, requirements.txt), README, docs, config files
|
|
1769
|
+
2. **Source Discovery**: Navigate src/, lib/, components/, utils/, types/
|
|
1770
|
+
3. **Validation**: Explore test/, examples/, demos/ directories
|
|
1771
|
+
|
|
1772
|
+
**DIRECTORY PRIORITIES:**
|
|
1773
|
+
- HIGH: src/, lib/, components/, utils/, types/ (Core implementations)
|
|
1774
|
+
- MEDIUM: docs/, examples/, config/ (Context/documentation)
|
|
1775
|
+
- TEST: test/, __tests__/, spec/ (Quality/patterns)
|
|
1776
|
+
|
|
1777
|
+
**NAVIGATION STRATEGY:**
|
|
1778
|
+
- Start with root for project overview
|
|
1779
|
+
- Target core implementation directories
|
|
1780
|
+
- Extract promising files via ${TOOL_NAMES.FETCH_GITHUB_FILE_CONTENT}
|
|
1781
|
+
- Cross-reference with tests for usage patterns
|
|
1782
|
+
|
|
1783
|
+
**RESULT TARGETS:**
|
|
1784
|
+
- 1-10 results: IDEAL - Focused exploration
|
|
1785
|
+
- 11-50 results: MANAGEABLE - Prioritize by conventions
|
|
1786
|
+
- 50+ results: TOO BROAD - Explore subdirectories
|
|
1787
|
+
|
|
1788
|
+
**ENHANCED BRANCH FALLBACK:**
|
|
1789
|
+
Automatically tries multiple fallback strategies:
|
|
1790
|
+
1. Specified branch → main → master → develop → trunk (with ref parameter)
|
|
1791
|
+
2. If all fail: Try without ref parameter (uses repository default branch)
|
|
1792
|
+
Handles branch discovery failures gracefully with comprehensive error reporting.
|
|
1793
|
+
|
|
1794
|
+
**OUTPUT:** Architectural map to guide intelligent code extraction.
|
|
1795
|
+
|
|
1796
|
+
**NEVER:**
|
|
1797
|
+
- Explore without calling ${TOOL_NAMES.VIEW_REPOSITORY} first
|
|
1798
|
+
- Use hardcoded branch names without verification
|
|
1799
|
+
- Assume default branch names across repositories`;
|
|
1800
|
+
|
|
1801
|
+
function registerViewRepositoryStructureTool(server) {
|
|
1802
|
+
server.tool(TOOL_NAMES.VIEW_REPOSITORY_STRUCTURE, VIEW_REPOSITORY_STRUCTURE_DESCRIPTION, {
|
|
1803
|
+
owner: z
|
|
1804
|
+
.string()
|
|
1805
|
+
.describe(`Specify the repository owner/organization. This can be obtained using the appropriate tool for fetching user organizations.`),
|
|
1806
|
+
repo: z.string().describe('The name of the GitHub repository'),
|
|
1807
|
+
branch: z
|
|
1808
|
+
.string()
|
|
1809
|
+
.describe(`MANDATORY: Specify the branch to explore (e.g., 'main', 'master', 'develop'). Must be obtained from ${TOOL_NAMES.VIEW_REPOSITORY} first. Never explore without explicit branch specification.`),
|
|
1810
|
+
path: z
|
|
1811
|
+
.string()
|
|
1812
|
+
.optional()
|
|
1813
|
+
.default('')
|
|
1814
|
+
.describe('The path within the repository to view the structure from. Defaults to the root of the repository. Allows for iterative exploration of the repository structure.'),
|
|
1815
|
+
}, async (args) => {
|
|
1816
|
+
try {
|
|
1817
|
+
return await viewRepositoryStructure(args);
|
|
1818
|
+
}
|
|
1819
|
+
catch (error) {
|
|
1820
|
+
return {
|
|
1821
|
+
content: [
|
|
1822
|
+
{
|
|
1823
|
+
type: 'text',
|
|
1824
|
+
text: `Failed to view repository structure: ${error.message}`,
|
|
1825
|
+
},
|
|
1826
|
+
],
|
|
1827
|
+
isError: true,
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
const SEARCH_GITHUB_ISSUES_DESCRIPTION = `Advanced GitHub issues search for problem discovery and solution research.
|
|
1834
|
+
|
|
1835
|
+
**SEARCH STRATEGY:**
|
|
1836
|
+
1. **Single Keywords First**: "bug", "feature", "documentation"
|
|
1837
|
+
2. **Then Combine**: "bug fix", "feature request" (only if needed)
|
|
1838
|
+
3. **Never Start Complex**: Avoid "critical performance bug in production"
|
|
1839
|
+
|
|
1840
|
+
**CORE PURPOSE:**
|
|
1841
|
+
- Problem discovery and existing issue research
|
|
1842
|
+
- Solution research and community insights
|
|
1843
|
+
- Development planning and quality assurance
|
|
1844
|
+
|
|
1845
|
+
**SEARCH METHODOLOGY:**
|
|
1846
|
+
- **Phase 1**: Core discovery ("authentication", "error") → understand patterns
|
|
1847
|
+
- **Phase 2**: Context expansion ("authentication JWT", "error handling")
|
|
1848
|
+
- **Phase 3**: Solution focus ("authentication bug resolved")
|
|
1849
|
+
|
|
1850
|
+
**RESULT TARGETS:**
|
|
1851
|
+
- 0 results: Try broader terms, remove filters
|
|
1852
|
+
- 1-20 results: IDEAL - Deep analysis of patterns and solutions
|
|
1853
|
+
- 21-100 results: GOOD - Add specificity or filters
|
|
1854
|
+
- 100+ results: Add specific terms or state/label filters
|
|
1855
|
+
|
|
1856
|
+
**TERM SELECTION:**
|
|
1857
|
+
- Actual error messages: "TypeError", "404", "connection refused"
|
|
1858
|
+
- Status indicators: "open", "closed", "resolved", "duplicate"
|
|
1859
|
+
- Impact levels: "critical", "bug", "enhancement", "question"
|
|
1860
|
+
- Component names: "API", "frontend", "database", "deployment"
|
|
1861
|
+
|
|
1862
|
+
**FILTERING STRATEGY:**
|
|
1863
|
+
- State filter: "open" for current issues, "closed" for resolved patterns
|
|
1864
|
+
- Label filter: Severity ("bug", "enhancement", "documentation")
|
|
1865
|
+
- Assignee filter: Issues handled by specific maintainers
|
|
1866
|
+
- Author filter: Track issues from particular users
|
|
1867
|
+
- Date filters: Recent issues or historical analysis
|
|
1868
|
+
|
|
1869
|
+
**CROSS-REFERENCE APPROACH:**
|
|
1870
|
+
- Combine with ${TOOL_NAMES.SEARCH_GITHUB_CODE} for implementation details
|
|
1871
|
+
- Use with ${TOOL_NAMES.SEARCH_GITHUB_COMMITS} for fix implementations
|
|
1872
|
+
- Cross-reference with ${TOOL_NAMES.SEARCH_GITHUB_PULL_REQUESTS} for solutions
|
|
1873
|
+
- Explore ${TOOL_NAMES.SEARCH_GITHUB_DISCUSSIONS} for community insights
|
|
1874
|
+
|
|
1875
|
+
**PATTERN ANALYSIS:** Focus on issue resolution patterns and community feedback for development insights.`;
|
|
1876
|
+
|
|
1877
|
+
function registerSearchGitHubIssuesTool(server) {
|
|
1878
|
+
server.tool(TOOL_NAMES.SEARCH_GITHUB_ISSUES, SEARCH_GITHUB_ISSUES_DESCRIPTION, {
|
|
1879
|
+
query: z
|
|
1880
|
+
.string()
|
|
1881
|
+
.describe("The search query to find issues (e.g., 'bug fix', 'feature request', 'documentation')"),
|
|
1882
|
+
owner: z
|
|
1883
|
+
.string()
|
|
1884
|
+
.describe("Filter by repository owner/organization (e.g., 'example-org')"),
|
|
1885
|
+
repo: z
|
|
1886
|
+
.string()
|
|
1887
|
+
.optional()
|
|
1888
|
+
.describe("Filter by specific repository name (e.g., 'cli/cli'). Note: Always do exploratory search without repo filter first"),
|
|
1889
|
+
app: z.string().optional().describe('Filter by GitHub App author'),
|
|
1890
|
+
archived: z
|
|
1891
|
+
.boolean()
|
|
1892
|
+
.optional()
|
|
1893
|
+
.describe('Filter based on the repository archived state'),
|
|
1894
|
+
assignee: z.string().optional().describe('Filter by assignee'),
|
|
1895
|
+
author: z.string().optional().describe('Filter by issue author'),
|
|
1896
|
+
closed: z.string().optional().describe('Filter on closed at date'),
|
|
1897
|
+
commenter: z
|
|
1898
|
+
.string()
|
|
1899
|
+
.optional()
|
|
1900
|
+
.describe('Filter based on comments by user'),
|
|
1901
|
+
comments: z.number().optional().describe('Filter on number of comments'),
|
|
1902
|
+
created: z
|
|
1903
|
+
.string()
|
|
1904
|
+
.optional()
|
|
1905
|
+
.describe("Filter based on created at date (e.g., '>2022-01-01', '<2023-12-31')"),
|
|
1906
|
+
includePrs: z
|
|
1907
|
+
.boolean()
|
|
1908
|
+
.optional()
|
|
1909
|
+
.describe('Include pull requests in results'),
|
|
1910
|
+
interactions: z
|
|
1911
|
+
.number()
|
|
1912
|
+
.optional()
|
|
1913
|
+
.describe('Filter on number of reactions and comments'),
|
|
1914
|
+
involves: z
|
|
1915
|
+
.string()
|
|
1916
|
+
.optional()
|
|
1917
|
+
.describe('Filter based on involvement of user'),
|
|
1918
|
+
labels: z
|
|
1919
|
+
.string()
|
|
1920
|
+
.optional()
|
|
1921
|
+
.describe("Filter by labels (e.g., 'bug', 'enhancement', 'documentation')"),
|
|
1922
|
+
language: z
|
|
1923
|
+
.string()
|
|
1924
|
+
.optional()
|
|
1925
|
+
.describe('Filter based on the coding language'),
|
|
1926
|
+
locked: z
|
|
1927
|
+
.boolean()
|
|
1928
|
+
.optional()
|
|
1929
|
+
.describe('Filter on locked conversation status'),
|
|
1930
|
+
match: z
|
|
1931
|
+
.enum(['title', 'body', 'comments'])
|
|
1932
|
+
.optional()
|
|
1933
|
+
.describe('Restrict search to specific field of issue'),
|
|
1934
|
+
mentions: z.string().optional().describe('Filter based on user mentions'),
|
|
1935
|
+
milestone: z.string().optional().describe('Filter by milestone title'),
|
|
1936
|
+
noAssignee: z.boolean().optional().describe('Filter on missing assignee'),
|
|
1937
|
+
noLabel: z.boolean().optional().describe('Filter on missing label'),
|
|
1938
|
+
noMilestone: z
|
|
1939
|
+
.boolean()
|
|
1940
|
+
.optional()
|
|
1941
|
+
.describe('Filter on missing milestone'),
|
|
1942
|
+
noProject: z.boolean().optional().describe('Filter on missing project'),
|
|
1943
|
+
project: z
|
|
1944
|
+
.string()
|
|
1945
|
+
.optional()
|
|
1946
|
+
.describe('Filter on project board owner/number'),
|
|
1947
|
+
reactions: z
|
|
1948
|
+
.number()
|
|
1949
|
+
.optional()
|
|
1950
|
+
.describe('Filter on number of reactions'),
|
|
1951
|
+
state: z
|
|
1952
|
+
.enum(['open', 'closed'])
|
|
1953
|
+
.optional()
|
|
1954
|
+
.describe('Filter based on issue state'),
|
|
1955
|
+
teamMentions: z
|
|
1956
|
+
.string()
|
|
1957
|
+
.optional()
|
|
1958
|
+
.describe('Filter based on team mentions'),
|
|
1959
|
+
updated: z.string().optional().describe('Filter on last updated at date'),
|
|
1960
|
+
visibility: z
|
|
1961
|
+
.enum(['public', 'private', 'internal'])
|
|
1962
|
+
.optional()
|
|
1963
|
+
.describe('Filter based on repository visibility'),
|
|
1964
|
+
limit: z
|
|
1965
|
+
.number()
|
|
1966
|
+
.optional()
|
|
1967
|
+
.default(50)
|
|
1968
|
+
.describe('Maximum number of issues to return (default: 50)'),
|
|
1969
|
+
sort: z
|
|
1970
|
+
.enum([
|
|
1971
|
+
'comments',
|
|
1972
|
+
'created',
|
|
1973
|
+
'interactions',
|
|
1974
|
+
'reactions',
|
|
1975
|
+
'reactions-+1',
|
|
1976
|
+
'reactions--1',
|
|
1977
|
+
'reactions-heart',
|
|
1978
|
+
'reactions-smile',
|
|
1979
|
+
'reactions-tada',
|
|
1980
|
+
'reactions-thinking_face',
|
|
1981
|
+
'updated',
|
|
1982
|
+
'best-match',
|
|
1983
|
+
])
|
|
1984
|
+
.optional()
|
|
1985
|
+
.describe('Sort issues by specified criteria'),
|
|
1986
|
+
order: z
|
|
1987
|
+
.enum(['asc', 'desc'])
|
|
1988
|
+
.optional()
|
|
1989
|
+
.default('desc')
|
|
1990
|
+
.describe('Order of results returned (default: desc)'),
|
|
1991
|
+
}, async (args) => {
|
|
1992
|
+
try {
|
|
1993
|
+
return await searchGitHubIssues(args);
|
|
1994
|
+
}
|
|
1995
|
+
catch (error) {
|
|
1996
|
+
return {
|
|
1997
|
+
content: [
|
|
1998
|
+
{
|
|
1999
|
+
type: 'text',
|
|
2000
|
+
text: `Failed to search GitHub issues: ${error.message}`,
|
|
2001
|
+
},
|
|
2002
|
+
],
|
|
2003
|
+
isError: true,
|
|
2004
|
+
};
|
|
2005
|
+
}
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
const SEARCH_GITHUB_DISCUSSIONS_DESCRIPTION = `GitHub discussions search for community knowledge discovery.
|
|
2010
|
+
|
|
2011
|
+
**CORE PURPOSE:**
|
|
2012
|
+
Access community Q&A, tutorials, and best practices for learning and problem-solving.
|
|
2013
|
+
|
|
2014
|
+
**DISCOVERY WORKFLOW:**
|
|
2015
|
+
1. Use ${TOOL_NAMES.NPM_VIEW} for packages → get repo URL
|
|
2016
|
+
2. Use ${TOOL_NAMES.SEARCH_GITHUB_REPOS} for projects → get owner/repo
|
|
2017
|
+
3. Search discussions with discovered owner/repo context
|
|
2018
|
+
|
|
2019
|
+
**SEARCH STRATEGY:**
|
|
2020
|
+
1. **Start Lean**: Single keywords ("help", "tutorial", "authentication")
|
|
2021
|
+
2. **Build Complexity**: Combinations if needed ("help deployment")
|
|
2022
|
+
3. **Avoid Complex**: Don't start with full phrases
|
|
2023
|
+
|
|
2024
|
+
**RESULT TARGETS:**
|
|
2025
|
+
- 1-15 results: IDEAL - Deep analysis opportunities
|
|
2026
|
+
- 16-50 results: GOOD - Manageable scope
|
|
2027
|
+
- 51-100 results: BROAD - Add category filters
|
|
2028
|
+
- 100+ results: TOO GENERIC - Refine terms
|
|
2029
|
+
|
|
2030
|
+
**KEY FILTERS:**
|
|
2031
|
+
- **Answered**: True for validated solutions
|
|
2032
|
+
- **Category**: Q&A, General, Show and Tell
|
|
2033
|
+
- **Author/maintainer**: For authoritative responses
|
|
2034
|
+
- **Date filters**: Recent vs historical discussions
|
|
2035
|
+
|
|
2036
|
+
**SEARCH PATTERNS:**
|
|
2037
|
+
- **Problem solving**: "deployment help", "authentication tutorial"
|
|
2038
|
+
- **Best practices**: "testing patterns", "architecture decisions"
|
|
2039
|
+
- **Community insights**: "performance tips", "migration guide"
|
|
2040
|
+
|
|
2041
|
+
**QUALITY INDICATORS:**
|
|
2042
|
+
- Answered discussions for validated solutions
|
|
2043
|
+
- Maintainer participation for authoritative guidance
|
|
2044
|
+
- Recent activity for current relevance
|
|
2045
|
+
|
|
2046
|
+
**FALLBACK STRATEGY:**
|
|
2047
|
+
If no discussions found, try ${TOOL_NAMES.SEARCH_GITHUB_ISSUES} for alternative community insights.
|
|
2048
|
+
|
|
2049
|
+
**OUTPUT:** Community-validated knowledge and solutions for development challenges.`;
|
|
2050
|
+
|
|
2051
|
+
function registerSearchGitHubDiscussionsTool(server) {
|
|
2052
|
+
server.tool(TOOL_NAMES.SEARCH_GITHUB_DISCUSSIONS, SEARCH_GITHUB_DISCUSSIONS_DESCRIPTION, {
|
|
2053
|
+
query: z
|
|
2054
|
+
.string()
|
|
2055
|
+
.describe("The search query to find discussions (e.g., 'deployment help', 'authentication tutorial', 'best practices')"),
|
|
2056
|
+
owner: z
|
|
2057
|
+
.string()
|
|
2058
|
+
.describe("Filter by repository owner/organization (e.g., 'example-org')"),
|
|
2059
|
+
repo: z
|
|
2060
|
+
.string()
|
|
2061
|
+
.optional()
|
|
2062
|
+
.describe("Filter by specific repository name (e.g., 'cli/cli'). Note: Always do exploratory search without repo filter first"),
|
|
2063
|
+
author: z.string().optional().describe('Filter by discussion author'),
|
|
2064
|
+
assignee: z.string().optional().describe('Filter by assignee'),
|
|
2065
|
+
mentions: z.string().optional().describe('Filter based on user mentions'),
|
|
2066
|
+
commenter: z
|
|
2067
|
+
.string()
|
|
2068
|
+
.optional()
|
|
2069
|
+
.describe('Filter based on comments by user'),
|
|
2070
|
+
involves: z
|
|
2071
|
+
.string()
|
|
2072
|
+
.optional()
|
|
2073
|
+
.describe('Filter based on involvement of user'),
|
|
2074
|
+
category: z
|
|
2075
|
+
.string()
|
|
2076
|
+
.optional()
|
|
2077
|
+
.describe("Filter by discussion category (e.g., 'Q&A', 'General', 'Show and Tell')"),
|
|
2078
|
+
answered: z
|
|
2079
|
+
.boolean()
|
|
2080
|
+
.optional()
|
|
2081
|
+
.describe('Filter by answered state (true for answered discussions only)'),
|
|
2082
|
+
created: z
|
|
2083
|
+
.string()
|
|
2084
|
+
.optional()
|
|
2085
|
+
.describe("Filter based on created date (e.g., '>2022-01-01', '<2023-12-31')"),
|
|
2086
|
+
updated: z.string().optional().describe('Filter on last updated date'),
|
|
2087
|
+
limit: z
|
|
2088
|
+
.number()
|
|
2089
|
+
.optional()
|
|
2090
|
+
.default(50)
|
|
2091
|
+
.describe('Maximum number of discussions to return (default: 50)'),
|
|
2092
|
+
sort: z
|
|
2093
|
+
.enum([
|
|
2094
|
+
'comments',
|
|
2095
|
+
'reactions',
|
|
2096
|
+
'reactions-+1',
|
|
2097
|
+
'reactions--1',
|
|
2098
|
+
'reactions-smile',
|
|
2099
|
+
'reactions-thinking_face',
|
|
2100
|
+
'reactions-heart',
|
|
2101
|
+
'reactions-tada',
|
|
2102
|
+
'interactions',
|
|
2103
|
+
'created',
|
|
2104
|
+
'updated',
|
|
2105
|
+
])
|
|
2106
|
+
.optional()
|
|
2107
|
+
.describe('Sort discussions by specified criteria'),
|
|
2108
|
+
order: z
|
|
2109
|
+
.enum(['asc', 'desc'])
|
|
2110
|
+
.optional()
|
|
2111
|
+
.default('desc')
|
|
2112
|
+
.describe('Order of results returned (default: desc)'),
|
|
2113
|
+
}, async (args) => {
|
|
2114
|
+
try {
|
|
2115
|
+
return await searchGitHubDiscussions(args);
|
|
2116
|
+
}
|
|
2117
|
+
catch (error) {
|
|
2118
|
+
return {
|
|
2119
|
+
content: [
|
|
2120
|
+
{
|
|
2121
|
+
type: 'text',
|
|
2122
|
+
text: `Failed to search GitHub discussions: ${error.message}`,
|
|
2123
|
+
},
|
|
2124
|
+
],
|
|
2125
|
+
isError: true,
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
const SEARCH_GITHUB_TOPICS_DESCRIPTION = `**VITAL FOUNDATION TOOL** for effective GitHub discovery - provides semantic context that makes all other GitHub searches targeted and successful.
|
|
2132
|
+
|
|
2133
|
+
**WHY ESSENTIAL:**
|
|
2134
|
+
- **Term Discovery**: Find correct terminology and keywords before searching repositories
|
|
2135
|
+
- **Quality Signals**: Featured/curated topics = community-validated, battle-tested projects
|
|
2136
|
+
- **Repository Filters**: Use topic names directly in ${TOOL_NAMES.SEARCH_GITHUB_REPOS} for precise targeting
|
|
2137
|
+
- **Ecosystem Mapping**: Understand technology landscapes with 100M+ repositories of production code
|
|
2138
|
+
|
|
2139
|
+
**CRITICAL WORKFLOW - Topics → Repositories:**
|
|
2140
|
+
1. **Search topics first** → discover proper terminology (e.g., "machine-learning" not "AI")
|
|
2141
|
+
2. **Use topic names as filters** → in ${TOOL_NAMES.SEARCH_GITHUB_REPOS} for targeted results
|
|
2142
|
+
3. **Quality validation** → featured topics = GitHub-promoted, curated = community-maintained
|
|
2143
|
+
|
|
2144
|
+
**SEARCH STRATEGY:**
|
|
2145
|
+
- Start simple: "react", "docker", "cli"
|
|
2146
|
+
- Combine when needed: "machine-learning python"
|
|
2147
|
+
- Avoid complex phrases: keep focused and discoverable
|
|
2148
|
+
|
|
2149
|
+
**EXAMPLES:**
|
|
2150
|
+
- Search "javascript" → find topics like "typescript", "nodejs", "frontend"
|
|
2151
|
+
- Use discovered topics → search repos with topic:"typescript" for quality examples
|
|
2152
|
+
- Featured topics → guaranteed high-quality repository collections
|
|
2153
|
+
|
|
2154
|
+
**RESULT OPTIMIZATION:**
|
|
2155
|
+
- 1-10 results: IDEAL for deep analysis
|
|
2156
|
+
- 10+ results: Add featured/curated filters
|
|
2157
|
+
- Use repository count as maturity indicator (>10K = established, 1K-10K = growing)
|
|
2158
|
+
|
|
2159
|
+
**OUTPUT:** Strategic foundation for all GitHub discovery - transforms random searches into targeted, quality-focused repository discovery.`;
|
|
2160
|
+
|
|
2161
|
+
function registerSearchGitHubTopicsTool(server) {
|
|
2162
|
+
server.tool(TOOL_NAMES.SEARCH_GITHUB_TOPICS, SEARCH_GITHUB_TOPICS_DESCRIPTION, {
|
|
2163
|
+
query: z
|
|
2164
|
+
.string()
|
|
2165
|
+
.describe("The search query to find topics (e.g., 'machine learning', 'react', 'devops', 'blockchain')"),
|
|
2166
|
+
owner: z
|
|
2167
|
+
.string()
|
|
2168
|
+
.describe("Filter by repository owner/organization (e.g., 'example-org') obtained from the appropriate tool for fetching user organizations"),
|
|
2169
|
+
featured: z
|
|
2170
|
+
.boolean()
|
|
2171
|
+
.optional()
|
|
2172
|
+
.describe('Filter for featured topics curated by GitHub'),
|
|
2173
|
+
curated: z
|
|
2174
|
+
.boolean()
|
|
2175
|
+
.optional()
|
|
2176
|
+
.describe('Filter for topics curated by the GitHub community'),
|
|
2177
|
+
repositories: z
|
|
2178
|
+
.string()
|
|
2179
|
+
.optional()
|
|
2180
|
+
.describe("Filter by number of repositories using this topic (e.g., '>1000', '<500')"),
|
|
2181
|
+
created: z
|
|
2182
|
+
.string()
|
|
2183
|
+
.optional()
|
|
2184
|
+
.describe("Filter based on topic creation date (e.g., '>2022-01-01', '<2023-12-31')"),
|
|
2185
|
+
limit: z
|
|
2186
|
+
.number()
|
|
2187
|
+
.optional()
|
|
2188
|
+
.default(50)
|
|
2189
|
+
.describe('Maximum number of topics to return (default: 50)'),
|
|
2190
|
+
sort: z
|
|
2191
|
+
.enum(['featured', 'repositories', 'created', 'updated'])
|
|
2192
|
+
.optional()
|
|
2193
|
+
.describe('Sort topics by specified criteria (default: best-match)'),
|
|
2194
|
+
order: z
|
|
2195
|
+
.enum(['asc', 'desc'])
|
|
2196
|
+
.optional()
|
|
2197
|
+
.default('desc')
|
|
2198
|
+
.describe('Order of results returned (default: desc)'),
|
|
2199
|
+
}, async (args) => {
|
|
2200
|
+
try {
|
|
2201
|
+
return await searchGitHubTopics(args);
|
|
2202
|
+
}
|
|
2203
|
+
catch (error) {
|
|
2204
|
+
return {
|
|
2205
|
+
content: [
|
|
2206
|
+
{
|
|
2207
|
+
type: 'text',
|
|
2208
|
+
text: `Failed to search GitHub topics: ${error.message}`,
|
|
2209
|
+
},
|
|
2210
|
+
],
|
|
2211
|
+
isError: true,
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
const SEARCH_GITHUB_USERS_DESCRIPTION = `Advanced GitHub users and organizations search for developer discovery.
|
|
2218
|
+
|
|
2219
|
+
**SEARCH STRATEGY:**
|
|
2220
|
+
1. **Single Criteria First**: "react", "python", location
|
|
2221
|
+
2. **Then Combine**: "react javascript", location + language
|
|
2222
|
+
3. **Never Start Complex**: Avoid "senior react typescript developer with 5+ years"
|
|
2223
|
+
|
|
2224
|
+
**CORE PURPOSE:**
|
|
2225
|
+
- Expert discovery and community building
|
|
2226
|
+
- Collaboration and learning opportunities
|
|
2227
|
+
- Recruitment and network expansion
|
|
2228
|
+
|
|
2229
|
+
**SEARCH METHODOLOGY:**
|
|
2230
|
+
- **Phase 1**: Technology terms ("react", "python", "kubernetes") → analyze activity
|
|
2231
|
+
- **Phase 2**: Add context (location filters, experience indicators)
|
|
2232
|
+
- **Phase 3**: Specialized search (specific skills + activity filters)
|
|
2233
|
+
|
|
2234
|
+
**RESULT TARGETS:**
|
|
2235
|
+
- 0 results: Try broader terms, remove filters
|
|
2236
|
+
- 1-20 results: IDEAL - Analyze profiles for expertise
|
|
2237
|
+
- 21-100 results: GOOD - Add location or activity filters
|
|
2238
|
+
- 100+ results: Add specific terms or increase follower/repo filters
|
|
2239
|
+
|
|
2240
|
+
**TERM SELECTION:**
|
|
2241
|
+
- Technology: "javascript", "python", "golang", "rust"
|
|
2242
|
+
- Frameworks: "react", "vue", "django", "spring"
|
|
2243
|
+
- Roles: "devops", "frontend", "backend", "fullstack"
|
|
2244
|
+
- Context: "startup", "enterprise", "opensource"
|
|
2245
|
+
|
|
2246
|
+
**FILTERING STRATEGY:**
|
|
2247
|
+
- Type: "user" for individuals, "org" for organizations
|
|
2248
|
+
- Location: Find developers in specific regions
|
|
2249
|
+
- Language: Search by primary programming language
|
|
2250
|
+
- Followers: Find influential developers (">100", ">1000")
|
|
2251
|
+
- Repos: Find active contributors (">10", ">50")
|
|
2252
|
+
- Date: Recent activity or long-standing members
|
|
2253
|
+
|
|
2254
|
+
**DISCOVERY PATTERNS:**
|
|
2255
|
+
- **Technology Experts**: Language + high follower count
|
|
2256
|
+
- **Local Developers**: Location + technology
|
|
2257
|
+
- **Open Source Contributors**: High repo count + specific tech
|
|
2258
|
+
- **Industry Leaders**: Follower count + years of activity
|
|
2259
|
+
|
|
2260
|
+
**CROSS-REFERENCE STRATEGY:**
|
|
2261
|
+
- Find repository contributors and maintainers
|
|
2262
|
+
- Combine with ${TOOL_NAMES.SEARCH_GITHUB_REPOS} for project involvement
|
|
2263
|
+
- Explore user repositories for learning opportunities
|
|
2264
|
+
|
|
2265
|
+
**OUTPUT:** Developer and organization discovery for networking, learning, and collaboration.`;
|
|
2266
|
+
|
|
2267
|
+
function registerSearchGitHubUsersTool(server) {
|
|
2268
|
+
server.tool(TOOL_NAMES.SEARCH_GITHUB_USERS, SEARCH_GITHUB_USERS_DESCRIPTION, {
|
|
2269
|
+
query: z
|
|
2270
|
+
.string()
|
|
2271
|
+
.describe("The search query to find users/organizations (e.g., 'react developer', 'python', 'machine learning')"),
|
|
2272
|
+
owner: z
|
|
2273
|
+
.string()
|
|
2274
|
+
.describe("Filter by repository owner/organization (e.g., 'example-org') obtained from the appropriate tool for fetching user organizations"),
|
|
2275
|
+
type: z
|
|
2276
|
+
.enum(['user', 'org'])
|
|
2277
|
+
.optional()
|
|
2278
|
+
.describe('Filter by account type (user for individuals, org for organizations)'),
|
|
2279
|
+
location: z
|
|
2280
|
+
.string()
|
|
2281
|
+
.optional()
|
|
2282
|
+
.describe("Filter by location (e.g., 'San Francisco', 'London', 'Remote')"),
|
|
2283
|
+
language: z
|
|
2284
|
+
.string()
|
|
2285
|
+
.optional()
|
|
2286
|
+
.describe("Filter by primary programming language (e.g., 'javascript', 'python', 'java')"),
|
|
2287
|
+
followers: z
|
|
2288
|
+
.string()
|
|
2289
|
+
.optional()
|
|
2290
|
+
.describe("Filter by follower count (e.g., '>100', '>1000' for influential users)"),
|
|
2291
|
+
repos: z
|
|
2292
|
+
.string()
|
|
2293
|
+
.optional()
|
|
2294
|
+
.describe("Filter by repository count (e.g., '>10', '>50' for active contributors)"),
|
|
2295
|
+
created: z
|
|
2296
|
+
.string()
|
|
2297
|
+
.optional()
|
|
2298
|
+
.describe("Filter based on account creation date (e.g., '>2020-01-01', '<2023-12-31')"),
|
|
2299
|
+
limit: z
|
|
2300
|
+
.number()
|
|
2301
|
+
.optional()
|
|
2302
|
+
.default(50)
|
|
2303
|
+
.describe('Maximum number of users to return (default: 50)'),
|
|
2304
|
+
sort: z
|
|
2305
|
+
.enum(['followers', 'repositories', 'joined'])
|
|
2306
|
+
.optional()
|
|
2307
|
+
.describe('Sort users by specified criteria (default: best-match)'),
|
|
2308
|
+
order: z
|
|
2309
|
+
.enum(['asc', 'desc'])
|
|
2310
|
+
.optional()
|
|
2311
|
+
.default('desc')
|
|
2312
|
+
.describe('Order of results returned (default: desc)'),
|
|
2313
|
+
}, async (args) => {
|
|
2314
|
+
try {
|
|
2315
|
+
return await searchGitHubUsers(args);
|
|
2316
|
+
}
|
|
2317
|
+
catch (error) {
|
|
2318
|
+
return {
|
|
2319
|
+
content: [
|
|
2320
|
+
{
|
|
2321
|
+
type: 'text',
|
|
2322
|
+
text: `Failed to search GitHub users: ${error.message}`,
|
|
2323
|
+
},
|
|
2324
|
+
],
|
|
2325
|
+
isError: true,
|
|
2326
|
+
};
|
|
2327
|
+
}
|
|
2328
|
+
});
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
function registerAllTools(server) {
|
|
2332
|
+
// Register all tools
|
|
2333
|
+
registerSearchGitHubCodeTool(server);
|
|
2334
|
+
registerFetchGitHubFileContentTool(server);
|
|
2335
|
+
registerViewRepositoryTool(server);
|
|
2336
|
+
registerNpmViewTool(server);
|
|
2337
|
+
registerSearchGitHubReposTool(server);
|
|
2338
|
+
registerSearchGitHubCommitsTool(server);
|
|
2339
|
+
registerSearchGitHubPullRequestsTool(server);
|
|
2340
|
+
registerGetUserOrganizationsTool(server);
|
|
2341
|
+
registerNpmSearchTool(server);
|
|
2342
|
+
registerViewRepositoryStructureTool(server);
|
|
2343
|
+
registerSearchGitHubIssuesTool(server);
|
|
2344
|
+
registerSearchGitHubDiscussionsTool(server);
|
|
2345
|
+
registerSearchGitHubTopicsTool(server);
|
|
2346
|
+
registerSearchGitHubUsersTool(server);
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
const server = new McpServer({
|
|
2350
|
+
name: 'octocode-mcp',
|
|
2351
|
+
version: '1.0.0',
|
|
2352
|
+
description: `Code question assistant: Find, analyze, and explore any code in GitHub repositories and npm packages.
|
|
2353
|
+
Use for code examples, implementations, debugging, and understanding how libraries work.`,
|
|
2354
|
+
}, {
|
|
2355
|
+
capabilities: {
|
|
2356
|
+
tools: {},
|
|
2357
|
+
},
|
|
2358
|
+
instructions: `
|
|
2359
|
+
#PROMPT_SYSTEM_PROMPT
|
|
2360
|
+
${PROMPT_SYSTEM_PROMPT}`,
|
|
2361
|
+
});
|
|
2362
|
+
// Register all tools
|
|
2363
|
+
registerAllTools(server);
|
|
2364
|
+
const transport = new StdioServerTransport();
|
|
2365
|
+
await server.connect(transport);
|
|
2366
|
+
process.on('SIGINT', async () => {
|
|
2367
|
+
process.exit(0);
|
|
2368
|
+
});
|
|
2369
|
+
process.stdin.on('close', async () => {
|
|
2370
|
+
server.close();
|
|
2371
|
+
});
|