octocode-mcp 2.3.7 → 2.3.8
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/build/index.js +429 -413
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -238,10 +238,15 @@ async function executeGitHubCommand(command, args = [], options = {}) {
|
|
|
238
238
|
index > 1 &&
|
|
239
239
|
(arg.includes(':') || arg.startsWith('(')) &&
|
|
240
240
|
!arg.startsWith('--');
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
// and complex expressions like "(user:microsoft OR org:microsoft)"
|
|
241
|
+
// GitHub search qualifiers need special handling
|
|
242
|
+
// Most qualifiers can be passed as-is, but those with shell metacharacters need escaping
|
|
244
243
|
if (isGitHubQualifier) {
|
|
244
|
+
// Check if the qualifier contains shell metacharacters that need escaping
|
|
245
|
+
if (/[<>&|;`$\\]/.test(arg)) {
|
|
246
|
+
// Escape qualifiers that contain shell metacharacters like size:<1000, size:>500
|
|
247
|
+
return escapeShellArg(arg, shellConfig.type, false);
|
|
248
|
+
}
|
|
249
|
+
// Safe qualifiers like "language:typescript", "user:microsoft" can be passed as-is
|
|
245
250
|
return arg;
|
|
246
251
|
}
|
|
247
252
|
return escapeShellArg(arg, shellConfig.type, isMainQueryArgument);
|
|
@@ -303,6 +308,14 @@ async function executeCommand(fullCommand, type, options = {}, shellConfig) {
|
|
|
303
308
|
stderr.trim() !== '';
|
|
304
309
|
if (shouldTreatAsError) {
|
|
305
310
|
const errorType = type === 'npm' ? 'NPM command error' : 'GitHub CLI command error';
|
|
311
|
+
// Enhanced error messaging for common GitHub issues
|
|
312
|
+
if (type === 'github' && stderr.includes('404')) {
|
|
313
|
+
const isRepoNotFound = stderr.includes('Not Found');
|
|
314
|
+
const enhancedMessage = isRepoNotFound
|
|
315
|
+
? `${stderr}\n\nThis is often due to incorrect repository name. Use github_search_code to find the correct repository.`
|
|
316
|
+
: stderr;
|
|
317
|
+
return createErrorResult(errorType, new Error(enhancedMessage));
|
|
318
|
+
}
|
|
306
319
|
return createErrorResult(errorType, new Error(stderr));
|
|
307
320
|
}
|
|
308
321
|
// Try to parse stdout as JSON, fallback to string if not possible
|
|
@@ -545,7 +558,7 @@ function createSearchFailedError(type = 'code') {
|
|
|
545
558
|
}
|
|
546
559
|
|
|
547
560
|
const API_STATUS_CHECK_TOOL_NAME = 'apiStatusCheck';
|
|
548
|
-
const DESCRIPTION$9 = `Check GitHub and NPM authentication status.
|
|
561
|
+
const DESCRIPTION$9 = `Check GitHub and NPM authentication status and available organizations. Verifies API connections and returns user organizations for accessing private repositories. Essential for troubleshooting access issues. No parameters required.`;
|
|
549
562
|
// Helper function to parse execution results with proper typing
|
|
550
563
|
function parseExecResult(result) {
|
|
551
564
|
if (!result.isError && result.content?.[0]?.text) {
|
|
@@ -677,284 +690,34 @@ function registerApiStatusCheckTool(server) {
|
|
|
677
690
|
});
|
|
678
691
|
}
|
|
679
692
|
|
|
680
|
-
const GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME = 'githubViewRepoStructure';
|
|
681
|
-
const DESCRIPTION$8 = `Explore repository structure and navigate directories. Auto-detects branches and provides file/folder listings with size information. Parameters: owner (required - GitHub username/org), repo (required - repository name), branch (required), path (optional).`;
|
|
682
|
-
function registerViewRepositoryStructureTool(server) {
|
|
683
|
-
server.registerTool(GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME, {
|
|
684
|
-
description: DESCRIPTION$8,
|
|
685
|
-
inputSchema: {
|
|
686
|
-
owner: z
|
|
687
|
-
.string()
|
|
688
|
-
.min(1)
|
|
689
|
-
.max(100)
|
|
690
|
-
.regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/, 'Invalid GitHub username/org format')
|
|
691
|
-
.describe(`Repository owner/org name (e.g., 'microsoft', 'google', NOT 'microsoft/vscode')`),
|
|
692
|
-
repo: z
|
|
693
|
-
.string()
|
|
694
|
-
.min(1)
|
|
695
|
-
.max(100)
|
|
696
|
-
.regex(/^[a-zA-Z0-9._-]+$/, 'Invalid repository name format')
|
|
697
|
-
.describe('Repository name (case-sensitive)'),
|
|
698
|
-
branch: z
|
|
699
|
-
.string()
|
|
700
|
-
.min(1)
|
|
701
|
-
.max(255)
|
|
702
|
-
.regex(/^[^\s]+$/, 'Branch name cannot contain spaces')
|
|
703
|
-
.describe('Branch name. Falls back to default branch if not found'),
|
|
704
|
-
path: z
|
|
705
|
-
.string()
|
|
706
|
-
.optional()
|
|
707
|
-
.default('')
|
|
708
|
-
.refine(path => !path.includes('..'), 'Path traversal not allowed')
|
|
709
|
-
.refine(path => path.length <= 500, 'Path too long')
|
|
710
|
-
.describe('Directory path within repository. Leave empty for root.'),
|
|
711
|
-
},
|
|
712
|
-
annotations: {
|
|
713
|
-
title: 'GitHub Repository Explorer',
|
|
714
|
-
readOnlyHint: true,
|
|
715
|
-
destructiveHint: false,
|
|
716
|
-
idempotentHint: true,
|
|
717
|
-
openWorldHint: true,
|
|
718
|
-
},
|
|
719
|
-
}, async (args) => {
|
|
720
|
-
try {
|
|
721
|
-
const result = await viewRepositoryStructure(args);
|
|
722
|
-
return result;
|
|
723
|
-
}
|
|
724
|
-
catch (error) {
|
|
725
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
726
|
-
return createResult({
|
|
727
|
-
error: `Failed to explore repository. ${errorMessage}. Verify repository exists and is accessible`,
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
/**
|
|
733
|
-
* Views the structure of a GitHub repository at a specific path.
|
|
734
|
-
* Optimized for code analysis workflows with smart defaults and clear errors.
|
|
735
|
-
*/
|
|
736
|
-
async function viewRepositoryStructure(params) {
|
|
737
|
-
const cacheKey = generateCacheKey('gh-repo-structure', params);
|
|
738
|
-
return withCache(cacheKey, async () => {
|
|
739
|
-
const { owner, repo, branch, path = '' } = params;
|
|
740
|
-
try {
|
|
741
|
-
// Clean up path
|
|
742
|
-
const cleanPath = path.startsWith('/') ? path.substring(1) : path;
|
|
743
|
-
// Try the requested branch first, then fallback to main/master
|
|
744
|
-
const branchesToTry = await getSmartBranchFallback(owner, repo, branch);
|
|
745
|
-
let items = [];
|
|
746
|
-
let usedBranch = branch;
|
|
747
|
-
let lastError = null;
|
|
748
|
-
let attemptCount = 0;
|
|
749
|
-
const maxAttempts = branchesToTry.length;
|
|
750
|
-
const triedBranches = [];
|
|
751
|
-
for (const tryBranch of branchesToTry) {
|
|
752
|
-
if (attemptCount >= maxAttempts)
|
|
753
|
-
break;
|
|
754
|
-
attemptCount++;
|
|
755
|
-
triedBranches.push(tryBranch);
|
|
756
|
-
try {
|
|
757
|
-
const apiPath = `/repos/${owner}/${repo}/contents/${cleanPath}?ref=${tryBranch}`;
|
|
758
|
-
const result = await executeGitHubCommand('api', [apiPath], {
|
|
759
|
-
cache: false,
|
|
760
|
-
});
|
|
761
|
-
if (!result.isError) {
|
|
762
|
-
const execResult = JSON.parse(result.content[0].text);
|
|
763
|
-
const apiItems = execResult.result;
|
|
764
|
-
items = Array.isArray(apiItems) ? apiItems : [apiItems];
|
|
765
|
-
usedBranch = tryBranch;
|
|
766
|
-
break;
|
|
767
|
-
}
|
|
768
|
-
else {
|
|
769
|
-
lastError = new Error(result.content[0].text);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
catch (error) {
|
|
773
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
774
|
-
continue;
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
if (items.length === 0) {
|
|
778
|
-
// Check repository existence only after content fetch fails
|
|
779
|
-
const repoCheckResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
|
|
780
|
-
cache: false,
|
|
781
|
-
});
|
|
782
|
-
if (repoCheckResult.isError) {
|
|
783
|
-
const repoErrorMsg = repoCheckResult.content[0].text;
|
|
784
|
-
if (repoErrorMsg.includes('404')) {
|
|
785
|
-
return createResult({
|
|
786
|
-
error: `Repository "${owner}/${repo}" not found. It might have been deleted, renamed, or made private. Use github_search_code to find current location.`,
|
|
787
|
-
});
|
|
788
|
-
}
|
|
789
|
-
else if (repoErrorMsg.includes('403')) {
|
|
790
|
-
return createResult({
|
|
791
|
-
error: `Repository "${owner}/${repo}" exists but access is denied. Repository might be private or archived. Use api_status_check to verify permissions.`,
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
const errorMsg = lastError?.message || 'Unknown error';
|
|
796
|
-
if (errorMsg.includes('404') || errorMsg.includes('Not Found')) {
|
|
797
|
-
if (path) {
|
|
798
|
-
const searchSuggestion = await suggestPathSearchFallback(owner, path);
|
|
799
|
-
return createResult({
|
|
800
|
-
error: `Path "${path}" not found in any branch (tried: ${triedBranches.join(', ')}).${searchSuggestion}`,
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
else {
|
|
804
|
-
return createResult({
|
|
805
|
-
error: `Repository "${owner}/${repo}" structure not accessible. Repository might be empty, private, or you might not have sufficient permissions. Use github_search_code with owner="${owner}" to find accessible repositories.`,
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
else if (errorMsg.includes('403') || errorMsg.includes('Forbidden')) {
|
|
810
|
-
return createResult({
|
|
811
|
-
error: `Access denied to "${owner}/${repo}". Repository exists but might be private/archived. Use api_status_check to verify permissions, or github_search_code with owner="${owner}" to find accessible repositories.`,
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
|
-
else {
|
|
815
|
-
const searchSuggestion = path
|
|
816
|
-
? await suggestPathSearchFallback(owner, path)
|
|
817
|
-
: '';
|
|
818
|
-
return createResult({
|
|
819
|
-
error: `Failed to access "${owner}/${repo}": ${errorMsg}. Check network connection and repository permissions.${searchSuggestion}`,
|
|
820
|
-
});
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
// Limit total items to 100 for efficiency
|
|
824
|
-
const limitedItems = items.slice(0, 100);
|
|
825
|
-
// Sort: directories first, then alphabetically
|
|
826
|
-
limitedItems.sort((a, b) => {
|
|
827
|
-
if (a.type !== b.type) {
|
|
828
|
-
return a.type === 'dir' ? -1 : 1;
|
|
829
|
-
}
|
|
830
|
-
return a.name.localeCompare(b.name);
|
|
831
|
-
});
|
|
832
|
-
// Create simplified, token-efficient structure
|
|
833
|
-
const files = limitedItems
|
|
834
|
-
.filter(item => item.type === 'file')
|
|
835
|
-
.map(item => ({
|
|
836
|
-
name: item.name,
|
|
837
|
-
size: item.size,
|
|
838
|
-
url: item.path, // Use path for fetching
|
|
839
|
-
}));
|
|
840
|
-
const folders = limitedItems
|
|
841
|
-
.filter(item => item.type === 'dir')
|
|
842
|
-
.map(item => ({
|
|
843
|
-
name: item.name,
|
|
844
|
-
url: item.path, // Use path for browsing
|
|
845
|
-
}));
|
|
846
|
-
return createResult({
|
|
847
|
-
data: {
|
|
848
|
-
repository: `${owner}/${repo}`,
|
|
849
|
-
branch: usedBranch,
|
|
850
|
-
path: cleanPath || '/',
|
|
851
|
-
githubBasePath: `https://api.github.com/repos/${owner}/${repo}/contents/`,
|
|
852
|
-
files: {
|
|
853
|
-
count: files.length,
|
|
854
|
-
files: files,
|
|
855
|
-
},
|
|
856
|
-
folders: {
|
|
857
|
-
count: folders.length,
|
|
858
|
-
folders: folders,
|
|
859
|
-
},
|
|
860
|
-
},
|
|
861
|
-
});
|
|
862
|
-
}
|
|
863
|
-
catch (error) {
|
|
864
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
865
|
-
return createResult({
|
|
866
|
-
error: `Failed to access repository "${owner}/${repo}": ${errorMessage}. Verify repository name, permissions, and network connection.`,
|
|
867
|
-
});
|
|
868
|
-
}
|
|
869
|
-
});
|
|
870
|
-
}
|
|
871
|
-
/**
|
|
872
|
-
* Smart branch detection with automatic fallback to common branch names.
|
|
873
|
-
* Now includes more comprehensive branch detection and better error handling.
|
|
874
|
-
*/
|
|
875
|
-
async function getSmartBranchFallback(owner, repo, requestedBranch) {
|
|
876
|
-
const branches = new Set([requestedBranch]);
|
|
877
|
-
try {
|
|
878
|
-
// Try to get repository info to find default branch
|
|
879
|
-
const repoInfoResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
|
|
880
|
-
cache: false,
|
|
881
|
-
});
|
|
882
|
-
if (!repoInfoResult.isError) {
|
|
883
|
-
const execResult = JSON.parse(repoInfoResult.content[0].text);
|
|
884
|
-
const repoData = execResult.result;
|
|
885
|
-
const defaultBranch = repoData.default_branch;
|
|
886
|
-
if (defaultBranch) {
|
|
887
|
-
branches.add(defaultBranch);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
catch {
|
|
892
|
-
// If we can't get repo info, proceed with standard fallbacks
|
|
893
|
-
}
|
|
894
|
-
// Add only main/master as fallback branches
|
|
895
|
-
const commonBranches = ['main', 'master'];
|
|
896
|
-
commonBranches.forEach(branch => branches.add(branch));
|
|
897
|
-
// Convert Set back to array, with requested branch first
|
|
898
|
-
const branchesArray = Array.from(branches);
|
|
899
|
-
branchesArray.sort((a, b) => {
|
|
900
|
-
if (a === requestedBranch)
|
|
901
|
-
return -1;
|
|
902
|
-
if (b === requestedBranch)
|
|
903
|
-
return 1;
|
|
904
|
-
return 0;
|
|
905
|
-
});
|
|
906
|
-
return branchesArray;
|
|
907
|
-
}
|
|
908
|
-
// Helper function to suggest path search strategy
|
|
909
|
-
async function suggestPathSearchFallback(owner, path) {
|
|
910
|
-
try {
|
|
911
|
-
// Extract last path segment and try to find in same organization
|
|
912
|
-
const pathSegment = path.split('/').pop() || path;
|
|
913
|
-
const searchResult = await executeGitHubCommand('api', [
|
|
914
|
-
`/search/code?q=${encodeURIComponent(pathSegment)}+in:path+org:${owner}`,
|
|
915
|
-
], { cache: false });
|
|
916
|
-
if (!searchResult.isError) {
|
|
917
|
-
const results = JSON.parse(searchResult.content[0].text);
|
|
918
|
-
if (results.total_count > 0) {
|
|
919
|
-
const firstMatch = results.items[0];
|
|
920
|
-
return ` Directory might be in ${firstMatch.repository.full_name}. Try these searches:\n1. github_search_code with query="${pathSegment}" owner="${owner}"\n2. github_search_code with query="path:${path}" owner="${owner}"`;
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
catch {
|
|
925
|
-
// Fallback to generic message if search fails
|
|
926
|
-
}
|
|
927
|
-
return ` Try these searches:\n1. github_search_code with query="${path.split('/').pop()}" owner="${owner}"\n2. github_search_code with query="path:${path}"`;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
693
|
const GITHUB_GET_FILE_CONTENT_TOOL_NAME = 'githubGetFileContent';
|
|
931
|
-
const DESCRIPTION$
|
|
694
|
+
const DESCRIPTION$8 = `Fetch file content from GitHub repositories. Automatically handles branch fallback (main/master) and files up to 300KB. Returns decoded file content with metadata. Parameters: owner (required - GitHub username/org), repo (required - repository name), branch (required), filePath (required).`;
|
|
932
695
|
function registerFetchGitHubFileContentTool(server) {
|
|
933
696
|
server.registerTool(GITHUB_GET_FILE_CONTENT_TOOL_NAME, {
|
|
934
|
-
description: DESCRIPTION$
|
|
697
|
+
description: DESCRIPTION$8,
|
|
935
698
|
inputSchema: {
|
|
936
699
|
owner: z
|
|
937
700
|
.string()
|
|
938
701
|
.min(1)
|
|
939
702
|
.max(100)
|
|
940
703
|
.regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/)
|
|
941
|
-
.describe(`Repository owner/
|
|
704
|
+
.describe(`Repository owner/organization name (e.g., 'facebook', 'microsoft'). Do NOT include repository name here.`),
|
|
942
705
|
repo: z
|
|
943
706
|
.string()
|
|
944
707
|
.min(1)
|
|
945
708
|
.max(100)
|
|
946
709
|
.regex(/^[a-zA-Z0-9._-]+$/)
|
|
947
|
-
.describe(`Repository name only (e.g., '
|
|
710
|
+
.describe(`Repository name only (e.g., 'react', 'vscode'). Do NOT include owner/org prefix.`),
|
|
948
711
|
branch: z
|
|
949
712
|
.string()
|
|
950
713
|
.min(1)
|
|
951
714
|
.max(255)
|
|
952
715
|
.regex(/^[^\s]+$/)
|
|
953
|
-
.describe(`Branch name.
|
|
716
|
+
.describe(`Branch name (e.g., 'main', 'master'). Tool will automatically try 'main' and 'master' if specified branch is not found.`),
|
|
954
717
|
filePath: z
|
|
955
718
|
.string()
|
|
956
719
|
.min(1)
|
|
957
|
-
.describe(`
|
|
720
|
+
.describe(`File path from repository root (e.g., 'src/index.js', 'README.md', 'docs/api.md'). Do NOT start with slash.`),
|
|
958
721
|
},
|
|
959
722
|
annotations: {
|
|
960
723
|
title: 'GitHub File Content - Direct Access',
|
|
@@ -995,7 +758,7 @@ async function fetchGitHubFileContent(params) {
|
|
|
995
758
|
const repoErrorMsg = repoCheckResult.content[0].text;
|
|
996
759
|
if (repoErrorMsg.includes('404')) {
|
|
997
760
|
return createResult({
|
|
998
|
-
error: `Repository "${owner}/${repo}" not found.
|
|
761
|
+
error: `Repository "${owner}/${repo}" not found. This is often due to incorrect repository name. Steps to resolve:\n1. Use github_search_code with query="${filePath.split('/').pop()}" owner="${owner}" to find the correct repository\n2. Verify the exact repository name\n3. Check if the repository might have been renamed or moved`,
|
|
999
762
|
});
|
|
1000
763
|
}
|
|
1001
764
|
else if (repoErrorMsg.includes('403')) {
|
|
@@ -1146,46 +909,52 @@ async function suggestCodeSearchFallback(owner, filePath) {
|
|
|
1146
909
|
}
|
|
1147
910
|
|
|
1148
911
|
const GITHUB_SEARCH_CODE_TOOL_NAME = 'githubSearchCode';
|
|
1149
|
-
const DESCRIPTION$
|
|
912
|
+
const DESCRIPTION$7 = `Search code across GitHub repositories using GitHub's code search API.
|
|
913
|
+
|
|
914
|
+
Never use filters and flags on exploretory searches and on initial searches.
|
|
915
|
+
Use filters and flags on subsequent searches when you know what to search for or if the user asks for it explicitly.
|
|
916
|
+
Using too many flags might make miss relevant results. Use filters and flags to narrow down the results when getting too many.
|
|
917
|
+
|
|
918
|
+
Search Syntax - ALL terms must be present (AND logic):
|
|
919
|
+
The search finds files that contain ALL specified terms. Each space-separated term is required to be present in the same file.
|
|
920
|
+
- Multiple terms: All individual words must exist in the file
|
|
921
|
+
- Quoted phrases: Exact phrase matching for multi-word expressions
|
|
922
|
+
- Mixed terms: Combination of individual words AND exact phrases, all must be present
|
|
923
|
+
- Additional filters: Language, owner, repository, filename, extension, size and other flags supported
|
|
1150
924
|
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
-
|
|
1154
|
-
- Mixed: "async function" timeout → finds exact phrase "async function" AND word "timeout"
|
|
1155
|
-
- Complex: useState useEffect "custom hook" → finds "useState" AND "useEffect" AND exact phrase "custom hook"
|
|
1156
|
-
- Advanced: authentication authorization "jwt token" → finds "authentication" AND "authorization" AND exact phrase "jwt token"
|
|
1157
|
-
- Framework: "React.Component" "componentDidMount" → finds both exact phrases in same file
|
|
925
|
+
Key behavior: All search terms are combined with AND logic - every term must be found in the file.
|
|
926
|
+
Quotes create exact phrase matching, unquoted terms are individual word requirements.
|
|
927
|
+
Start with 1-2 terms, add more to narrow results. Use filters and flags for precision.
|
|
1158
928
|
|
|
1159
|
-
|
|
1160
|
-
Start with 1-2 terms, add more to narrow results. Use filters for precision.`;
|
|
929
|
+
`;
|
|
1161
930
|
function registerGitHubSearchCodeTool(server) {
|
|
1162
931
|
server.registerTool(GITHUB_SEARCH_CODE_TOOL_NAME, {
|
|
1163
|
-
description: DESCRIPTION$
|
|
932
|
+
description: DESCRIPTION$7,
|
|
1164
933
|
inputSchema: {
|
|
1165
934
|
query: z
|
|
1166
935
|
.string()
|
|
1167
936
|
.min(1)
|
|
1168
|
-
.describe('Search query with AND logic between terms. Multiple words require ALL to be present. Use quotes for exact phrases.
|
|
937
|
+
.describe('Search query with AND logic between terms. Multiple words require ALL to be present. Use quotes for exact phrases.'),
|
|
1169
938
|
language: z
|
|
1170
939
|
.string()
|
|
1171
940
|
.optional()
|
|
1172
|
-
.describe('Programming language filter
|
|
941
|
+
.describe('Programming language filter. Narrows search to specific language files. Use for language-specific searches.'),
|
|
1173
942
|
owner: z
|
|
1174
943
|
.union([z.string(), z.array(z.string())])
|
|
1175
944
|
.optional()
|
|
1176
|
-
.describe('Repository owner
|
|
945
|
+
.describe('Repository owner/organization name(s) to search within (e.g., "facebook", ["google", "microsoft"]). Use this to search within specific organizations. Do NOT use owner/repo format - just the organization/username.'),
|
|
1177
946
|
repo: z
|
|
1178
947
|
.union([z.string(), z.array(z.string())])
|
|
1179
948
|
.optional()
|
|
1180
|
-
.describe('Filter on specific repository(ies).
|
|
949
|
+
.describe('Filter on specific repository(ies). Must use full "owner/repo" format (e.g., "facebook/react", ["vuejs/vue", "angular/angular"]). Use this for searching within specific repositories.'),
|
|
1181
950
|
filename: z
|
|
1182
951
|
.string()
|
|
1183
952
|
.optional()
|
|
1184
|
-
.describe('Target specific filename or pattern.
|
|
953
|
+
.describe('Target specific filename or pattern. Use for file-specific searches.'),
|
|
1185
954
|
extension: z
|
|
1186
955
|
.string()
|
|
1187
956
|
.optional()
|
|
1188
|
-
.describe('File extension filter
|
|
957
|
+
.describe('File extension filter. Alternative to language parameter.'),
|
|
1189
958
|
match: z
|
|
1190
959
|
.union([z.enum(['file', 'path']), z.array(z.enum(['file', 'path']))])
|
|
1191
960
|
.optional()
|
|
@@ -1194,7 +963,7 @@ function registerGitHubSearchCodeTool(server) {
|
|
|
1194
963
|
.string()
|
|
1195
964
|
.regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid size format. Use: ">10", ">=5", "<100", "<=50", "10..100", or exact number "50"')
|
|
1196
965
|
.optional()
|
|
1197
|
-
.describe('File size filter in KB. Format: ">
|
|
966
|
+
.describe('File size filter in KB. Format: ">N" (larger than), "<N" (smaller than), "N..M" (range), "N" (exact).'),
|
|
1198
967
|
limit: z
|
|
1199
968
|
.number()
|
|
1200
969
|
.int()
|
|
@@ -1308,6 +1077,10 @@ function transformToOptimizedFormat$1(items) {
|
|
|
1308
1077
|
: [0, 0]) || [],
|
|
1309
1078
|
})) || [],
|
|
1310
1079
|
url: singleRepo ? item.path : simplifyGitHubUrl(item.url),
|
|
1080
|
+
repository: {
|
|
1081
|
+
nameWithOwner: item.repository.nameWithOwner,
|
|
1082
|
+
url: item.repository.url,
|
|
1083
|
+
},
|
|
1311
1084
|
}));
|
|
1312
1085
|
const result = {
|
|
1313
1086
|
items: optimizedItems,
|
|
@@ -1482,10 +1255,10 @@ function parseSearchQuery(query) {
|
|
|
1482
1255
|
}
|
|
1483
1256
|
|
|
1484
1257
|
const GITHUB_SEARCH_COMMITS_TOOL_NAME = 'githubSearchCommits';
|
|
1485
|
-
const DESCRIPTION$
|
|
1258
|
+
const DESCRIPTION$6 = `Search commit history across GitHub repositories. Find commits by message, author, date, or repository. Supports advanced filtering for comprehensive commit analysis. Returns commit SHA, message, author, and date information.`;
|
|
1486
1259
|
function registerGitHubSearchCommitsTool(server) {
|
|
1487
1260
|
server.registerTool(GITHUB_SEARCH_COMMITS_TOOL_NAME, {
|
|
1488
|
-
description: DESCRIPTION$
|
|
1261
|
+
description: DESCRIPTION$6,
|
|
1489
1262
|
inputSchema: {
|
|
1490
1263
|
query: z
|
|
1491
1264
|
.string()
|
|
@@ -1495,11 +1268,11 @@ function registerGitHubSearchCommitsTool(server) {
|
|
|
1495
1268
|
owner: z
|
|
1496
1269
|
.string()
|
|
1497
1270
|
.optional()
|
|
1498
|
-
.describe('Repository owner/
|
|
1271
|
+
.describe('Repository owner/organization name only (e.g., "facebook", "microsoft"). Do NOT include repository name. Must be used with repo parameter for repository-specific searches.'),
|
|
1499
1272
|
repo: z
|
|
1500
1273
|
.string()
|
|
1501
1274
|
.optional()
|
|
1502
|
-
.describe('Repository name only (e.g., "
|
|
1275
|
+
.describe('Repository name only (e.g., "react", "vscode"). Do NOT include owner prefix. Must be used together with owner parameter.'),
|
|
1503
1276
|
// Author filters
|
|
1504
1277
|
author: z
|
|
1505
1278
|
.string()
|
|
@@ -1749,10 +1522,10 @@ function buildGitHubCommitCliArgs(params) {
|
|
|
1749
1522
|
}
|
|
1750
1523
|
|
|
1751
1524
|
const GITHUB_SEARCH_ISSUES_TOOL_NAME = 'githubSearchIssues';
|
|
1752
|
-
const DESCRIPTION$
|
|
1525
|
+
const DESCRIPTION$5 = `Search GitHub issues for bug reports, feature requests, and discussions. Find issues by keywords, state, labels, author, or repository. Returns issue number, title, state, labels, and metadata for effective issue tracking and analysis.`;
|
|
1753
1526
|
function registerSearchGitHubIssuesTool(server) {
|
|
1754
1527
|
server.registerTool(GITHUB_SEARCH_ISSUES_TOOL_NAME, {
|
|
1755
|
-
description: DESCRIPTION$
|
|
1528
|
+
description: DESCRIPTION$5,
|
|
1756
1529
|
inputSchema: {
|
|
1757
1530
|
query: z
|
|
1758
1531
|
.string()
|
|
@@ -1762,11 +1535,11 @@ function registerSearchGitHubIssuesTool(server) {
|
|
|
1762
1535
|
.string()
|
|
1763
1536
|
.min(1)
|
|
1764
1537
|
.optional()
|
|
1765
|
-
.describe('Repository owner/
|
|
1538
|
+
.describe('Repository owner/organization name only (e.g., "facebook", "microsoft"). Do NOT include repository name. Must be used with repo parameter for repository-specific searches.'),
|
|
1766
1539
|
repo: z
|
|
1767
1540
|
.string()
|
|
1768
1541
|
.optional()
|
|
1769
|
-
.describe('Repository name only (e.g., "
|
|
1542
|
+
.describe('Repository name only (e.g., "react", "vscode"). Do NOT include owner prefix. Must be used together with owner parameter.'),
|
|
1770
1543
|
app: z
|
|
1771
1544
|
.string()
|
|
1772
1545
|
.optional()
|
|
@@ -1820,7 +1593,7 @@ function registerSearchGitHubIssuesTool(server) {
|
|
|
1820
1593
|
label: z
|
|
1821
1594
|
.union([z.string(), z.array(z.string())])
|
|
1822
1595
|
.optional()
|
|
1823
|
-
.describe('Label names
|
|
1596
|
+
.describe('Label names. Can be single string or array.'),
|
|
1824
1597
|
language: z.string().optional().describe('Repository language'),
|
|
1825
1598
|
locked: z.boolean().optional().describe('Conversation locked status'),
|
|
1826
1599
|
match: z
|
|
@@ -2083,10 +1856,10 @@ function buildGitHubIssuesAPICommand(params) {
|
|
|
2083
1856
|
|
|
2084
1857
|
// TODO: add PR commeents. e.g, gh pr view <PR_NUMBER_OR_URL_OR_BRANCH> --comments
|
|
2085
1858
|
const GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME = 'githubSearchPullRequests';
|
|
2086
|
-
const DESCRIPTION$
|
|
1859
|
+
const DESCRIPTION$4 = `Search GitHub pull requests for code changes, feature implementations, and bug fixes. Find PRs by keywords, state, author, review status, or repository. Returns PR number, title, state, branches, and review information for code review analysis.`;
|
|
2087
1860
|
function registerSearchGitHubPullRequestsTool(server) {
|
|
2088
1861
|
server.registerTool(GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME, {
|
|
2089
|
-
description: DESCRIPTION$
|
|
1862
|
+
description: DESCRIPTION$4,
|
|
2090
1863
|
inputSchema: {
|
|
2091
1864
|
query: z
|
|
2092
1865
|
.string()
|
|
@@ -2095,11 +1868,11 @@ function registerSearchGitHubPullRequestsTool(server) {
|
|
|
2095
1868
|
owner: z
|
|
2096
1869
|
.string()
|
|
2097
1870
|
.optional()
|
|
2098
|
-
.describe('Repository owner/
|
|
1871
|
+
.describe('Repository owner/organization name only (e.g., "facebook", "microsoft"). Do NOT include repository name. Must be used with repo parameter for repository-specific searches.'),
|
|
2099
1872
|
repo: z
|
|
2100
1873
|
.string()
|
|
2101
1874
|
.optional()
|
|
2102
|
-
.describe('Repository name only (e.g., "
|
|
1875
|
+
.describe('Repository name only (e.g., "react", "vscode"). Do NOT include owner prefix. Must be used together with owner parameter.'),
|
|
2103
1876
|
author: z.string().optional().describe('GitHub username of PR author'),
|
|
2104
1877
|
assignee: z.string().optional().describe('GitHub username of assignee'),
|
|
2105
1878
|
mentions: z.string().optional().describe('PRs mentioning this user'),
|
|
@@ -2118,10 +1891,7 @@ function registerSearchGitHubPullRequestsTool(server) {
|
|
|
2118
1891
|
.optional()
|
|
2119
1892
|
.describe('PR state. Default: all'),
|
|
2120
1893
|
head: z.string().optional().describe('Source branch name'),
|
|
2121
|
-
base: z
|
|
2122
|
-
.string()
|
|
2123
|
-
.optional()
|
|
2124
|
-
.describe('Target branch name (main, develop, etc.)'),
|
|
1894
|
+
base: z.string().optional().describe('Target branch name'),
|
|
2125
1895
|
language: z.string().optional().describe('Repository language'),
|
|
2126
1896
|
created: z
|
|
2127
1897
|
.string()
|
|
@@ -2409,26 +2179,27 @@ function buildGitHubPullRequestsAPICommand(params) {
|
|
|
2409
2179
|
* 4. Recent Quality:
|
|
2410
2180
|
* { stars: ">1000", created: ">2023-01-01", limit: 10 }
|
|
2411
2181
|
*
|
|
2182
|
+
* RESEARCH & EXPLORATION PATTERNS:
|
|
2183
|
+
*
|
|
2184
|
+
* 1. Topic-based Discovery (HIGHLY RECOMMENDED for unknown projects):
|
|
2185
|
+
* { topic: ["machine-learning", "nlp", "pytorch"], limit: 20 }
|
|
2186
|
+
* { topic: ["kubernetes", "monitoring"], stars: ">100", limit: 15 }
|
|
2187
|
+
*
|
|
2188
|
+
* 2. Exploratory Research Flow:
|
|
2189
|
+
* - Start with topics to discover repositories
|
|
2190
|
+
* - Then use githubViewRepoStructure to understand project layout
|
|
2191
|
+
* - Read README.md, docs/, and configuration files
|
|
2192
|
+
* - Finally use githubSearchCode for specific implementations
|
|
2193
|
+
*
|
|
2412
2194
|
* AVOID: OR queries + language filter, 5+ filters, multi-word OR
|
|
2413
2195
|
* TIP: Use limit parameter instead of adding more filters
|
|
2414
2196
|
*/
|
|
2415
2197
|
const GITHUB_SEARCH_REPOSITORIES_TOOL_NAME = 'githubSearchRepositories';
|
|
2416
|
-
const DESCRIPTION$
|
|
2417
|
-
|
|
2418
|
-
Search Logic (AND operation for multiple terms):
|
|
2419
|
-
- Multiple words: "react typescript" → repositories containing BOTH "react" AND "typescript"
|
|
2420
|
-
- Exact phrases: "web framework" → repositories with exact phrase "web framework"
|
|
2421
|
-
- Mixed: "machine learning" python → exact phrase "machine learning" AND word "python"
|
|
2422
|
-
- Filter combinations: language:javascript stars:>1000 → JavaScript repos with 1000+ stars
|
|
2198
|
+
const DESCRIPTION$3 = `Search GitHub repositories by name, description, topics, language, or organization. Find projects based on stars, forks, activity, and community metrics. Returns repository details including name, description, stars, language, and owner information for project discovery.
|
|
2423
2199
|
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
Examples:
|
|
2428
|
-
- Popular projects: stars:">1000" language:typescript
|
|
2429
|
-
- Beginner-friendly: good-first-issues:">5" stars:"100..5000"
|
|
2430
|
-
- Recent quality: created:">2023-01-01" stars:">500"
|
|
2431
|
-
- Organization repos: owner:microsoft language:python`;
|
|
2200
|
+
Search Syntax - ALL terms must be present (AND logic):
|
|
2201
|
+
Multiple search terms require ALL to be found in the repository (name, description, or README).
|
|
2202
|
+
Use quotes for exact phrase matching. Additional filters supported via parameters.`;
|
|
2432
2203
|
/**
|
|
2433
2204
|
* Extract owner/repo information from various query formats
|
|
2434
2205
|
*/
|
|
@@ -2461,21 +2232,21 @@ function extractOwnerRepoFromQuery(query) {
|
|
|
2461
2232
|
}
|
|
2462
2233
|
function registerSearchGitHubReposTool(server) {
|
|
2463
2234
|
server.registerTool(GITHUB_SEARCH_REPOSITORIES_TOOL_NAME, {
|
|
2464
|
-
description: DESCRIPTION$
|
|
2235
|
+
description: DESCRIPTION$3,
|
|
2465
2236
|
inputSchema: {
|
|
2466
2237
|
query: z
|
|
2467
2238
|
.string()
|
|
2468
2239
|
.optional()
|
|
2469
|
-
.describe('Search query with AND logic between terms. Multiple words require ALL to be present. Use quotes for exact phrases.
|
|
2240
|
+
.describe('Search query with AND logic between terms. Multiple words require ALL to be present. Use quotes for exact phrases.'),
|
|
2470
2241
|
// CORE FILTERS (GitHub CLI flags)
|
|
2471
2242
|
owner: z
|
|
2472
2243
|
.union([z.string(), z.array(z.string())])
|
|
2473
2244
|
.optional()
|
|
2474
|
-
.describe('Repository owner
|
|
2245
|
+
.describe('Repository owner/organization name(s) (e.g., "facebook", ["google", "microsoft"]). Search within specific organizations. Do NOT use owner/repo format - just the organization/username.'),
|
|
2475
2246
|
language: z
|
|
2476
2247
|
.string()
|
|
2477
2248
|
.optional()
|
|
2478
|
-
.describe('Programming language filter
|
|
2249
|
+
.describe('Programming language filter. Filters repositories by primary language. Essential for language-specific searches.'),
|
|
2479
2250
|
stars: z
|
|
2480
2251
|
.union([
|
|
2481
2252
|
z.number().int().min(0),
|
|
@@ -2484,11 +2255,11 @@ function registerSearchGitHubReposTool(server) {
|
|
|
2484
2255
|
.regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">1000", ">=500", "<100", "<=50", "10..100", or exact number "50"'),
|
|
2485
2256
|
])
|
|
2486
2257
|
.optional()
|
|
2487
|
-
.describe('Star count filter. Format: ">1000" (more than), ">=500" (more than or equal), "<100" (less than), "<=50" (less than or equal), "100..1000" (range), "500" (exact).
|
|
2258
|
+
.describe('Star count filter. Format: ">1000" (more than), ">=500" (more than or equal), "<100" (less than), "<=50" (less than or equal), "100..1000" (range), "500" (exact).'),
|
|
2488
2259
|
topic: z
|
|
2489
2260
|
.union([z.string(), z.array(z.string())])
|
|
2490
2261
|
.optional()
|
|
2491
|
-
.describe('Repository topics filter.
|
|
2262
|
+
.describe('Repository topics filter. Excellent for discovering projects and understanding repository ecosystems. Topics use kebab-case format.'),
|
|
2492
2263
|
forks: z
|
|
2493
2264
|
.union([
|
|
2494
2265
|
z.number().int().min(0),
|
|
@@ -2497,7 +2268,7 @@ function registerSearchGitHubReposTool(server) {
|
|
|
2497
2268
|
.regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">100", ">=50", "<10", "<=5", "10..100", or exact number "5"'),
|
|
2498
2269
|
])
|
|
2499
2270
|
.optional()
|
|
2500
|
-
.describe('Fork count filter. Format: ">100" (more than), ">=50" (more than or equal), "<10" (less than), "<=5" (less than or equal), "10..100" (range), "5" (exact).
|
|
2271
|
+
.describe('Fork count filter. Format: ">100" (more than), ">=50" (more than or equal), "<10" (less than), "<=5" (less than or equal), "10..100" (range), "5" (exact).'),
|
|
2501
2272
|
// Match CLI parameter name exactly
|
|
2502
2273
|
'number-topics': z
|
|
2503
2274
|
.union([
|
|
@@ -2507,40 +2278,40 @@ function registerSearchGitHubReposTool(server) {
|
|
|
2507
2278
|
.regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">5", ">=3", "<10", "<=2", "3..10", or exact number "5"'),
|
|
2508
2279
|
])
|
|
2509
2280
|
.optional()
|
|
2510
|
-
.describe('Number of topics filter. Format: ">5" (many topics), ">=3" (at least 3), "<10" (few topics), "1..3" (range), "5" (exact).
|
|
2281
|
+
.describe('Number of topics filter. Format: ">5" (many topics), ">=3" (at least 3), "<10" (few topics), "1..3" (range), "5" (exact).'),
|
|
2511
2282
|
// QUALITY & STATE FILTERS
|
|
2512
2283
|
license: z
|
|
2513
2284
|
.union([z.string(), z.array(z.string())])
|
|
2514
2285
|
.optional()
|
|
2515
|
-
.describe('License filter.
|
|
2286
|
+
.describe('License filter.'),
|
|
2516
2287
|
archived: z
|
|
2517
2288
|
.boolean()
|
|
2518
2289
|
.optional()
|
|
2519
|
-
.describe('Archive status filter. false (active repos only), true (archived repos only).
|
|
2290
|
+
.describe('Archive status filter. false (active repos only), true (archived repos only).'),
|
|
2520
2291
|
'include-forks': z
|
|
2521
2292
|
.enum(['false', 'true', 'only'])
|
|
2522
2293
|
.optional()
|
|
2523
|
-
.describe('Fork inclusion. "false" (exclude forks), "true" (include forks), "only" (forks only).
|
|
2294
|
+
.describe('Fork inclusion. "false" (exclude forks), "true" (include forks), "only" (forks only).'),
|
|
2524
2295
|
visibility: z
|
|
2525
2296
|
.enum(['public', 'private', 'internal'])
|
|
2526
2297
|
.optional()
|
|
2527
|
-
.describe('Repository visibility.
|
|
2298
|
+
.describe('Repository visibility.'),
|
|
2528
2299
|
// DATE & SIZE FILTERS
|
|
2529
2300
|
created: z
|
|
2530
2301
|
.string()
|
|
2531
2302
|
.regex(/^(>=?\d{4}-\d{2}-\d{2}|<=?\d{4}-\d{2}-\d{2}|\d{4}-\d{2}-\d{2}\.\.\d{4}-\d{2}-\d{2}|\d{4}-\d{2}-\d{2})$/, 'Invalid date format. Use: ">2020-01-01", ">=2020-01-01", "<2023-12-31", "<=2023-12-31", "2020-01-01..2023-12-31", or exact date "2023-01-01"')
|
|
2532
2303
|
.optional()
|
|
2533
|
-
.describe('Repository creation date filter. Format: ">2020-01-01" (after), ">=2020-01-01" (on or after), "<2023-12-31" (before), "<=2023-12-31" (on or before), "2020-01-01..2023-12-31" (range), "2023-01-01" (exact).
|
|
2304
|
+
.describe('Repository creation date filter. Format: ">2020-01-01" (after), ">=2020-01-01" (on or after), "<2023-12-31" (before), "<=2023-12-31" (on or before), "2020-01-01..2023-12-31" (range), "2023-01-01" (exact).'),
|
|
2534
2305
|
updated: z
|
|
2535
2306
|
.string()
|
|
2536
2307
|
.regex(/^(>=?\d{4}-\d{2}-\d{2}|<=?\d{4}-\d{2}-\d{2}|\d{4}-\d{2}-\d{2}\.\.\d{4}-\d{2}-\d{2}|\d{4}-\d{2}-\d{2})$/, 'Invalid date format. Use: ">2024-01-01", ">=2024-01-01", "<2022-01-01", "<=2022-01-01", "2023-01-01..2024-12-31", or exact date "2024-01-01"')
|
|
2537
2308
|
.optional()
|
|
2538
|
-
.describe('Last updated date filter. Format: ">2024-01-01" (recently updated), ">=2024-01-01" (on or after), "<2022-01-01" (not recently updated), "2023-01-01..2024-12-31" (range).
|
|
2309
|
+
.describe('Last updated date filter. Format: ">2024-01-01" (recently updated), ">=2024-01-01" (on or after), "<2022-01-01" (not recently updated), "2023-01-01..2024-12-31" (range).'),
|
|
2539
2310
|
size: z
|
|
2540
2311
|
.string()
|
|
2541
2312
|
.regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid size format. Use: ">1000", ">=500", "<100", "<=50", "100..1000", or exact number "500"')
|
|
2542
2313
|
.optional()
|
|
2543
|
-
.describe('Repository size filter in KB. Format: ">1000" (large projects), ">=500" (medium-large), "<100" (small projects), "<=50" (tiny), "100..1000" (medium range), "500" (exact).
|
|
2314
|
+
.describe('Repository size filter in KB. Format: ">1000" (large projects), ">=500" (medium-large), "<100" (small projects), "<=50" (tiny), "100..1000" (medium range), "500" (exact).'),
|
|
2544
2315
|
// COMMUNITY FILTERS - Match CLI parameter names exactly
|
|
2545
2316
|
'good-first-issues': z
|
|
2546
2317
|
.union([
|
|
@@ -2550,7 +2321,7 @@ function registerSearchGitHubReposTool(server) {
|
|
|
2550
2321
|
.regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: number, ">5", ">=10", "<20", "<=15", or "5..20"'),
|
|
2551
2322
|
])
|
|
2552
2323
|
.optional()
|
|
2553
|
-
.describe('Good first issues count. Format: ">5" (many beginner issues), "1..10" (some beginner issues).
|
|
2324
|
+
.describe('Good first issues count. Format: ">5" (many beginner issues), "1..10" (some beginner issues).'),
|
|
2554
2325
|
'help-wanted-issues': z
|
|
2555
2326
|
.union([
|
|
2556
2327
|
z.number().int().min(0),
|
|
@@ -2559,7 +2330,7 @@ function registerSearchGitHubReposTool(server) {
|
|
|
2559
2330
|
.regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: number, ">5", ">=10", "<20", "<=15", or "5..20"'),
|
|
2560
2331
|
])
|
|
2561
2332
|
.optional()
|
|
2562
|
-
.describe('Help wanted issues count. Format: ">10" (many help wanted), "1..5" (some help wanted).
|
|
2333
|
+
.describe('Help wanted issues count. Format: ">10" (many help wanted), "1..5" (some help wanted).'),
|
|
2563
2334
|
followers: z
|
|
2564
2335
|
.union([
|
|
2565
2336
|
z.number().int().min(0),
|
|
@@ -2568,12 +2339,15 @@ function registerSearchGitHubReposTool(server) {
|
|
|
2568
2339
|
.regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">1000", ">=500", "<100", "<=50", "100..1000", or exact number "500"'),
|
|
2569
2340
|
])
|
|
2570
2341
|
.optional()
|
|
2571
|
-
.describe('Repository owner followers count. Format: ">1000" (popular developers), ">=500" (established developers), "<100" (smaller developers), "100..1000" (range).
|
|
2572
|
-
// SEARCH SCOPE
|
|
2342
|
+
.describe('Repository owner followers count. Format: ">1000" (popular developers), ">=500" (established developers), "<100" (smaller developers), "100..1000" (range).'),
|
|
2343
|
+
// SEARCH SCOPE - Match CLI exactly
|
|
2573
2344
|
match: z
|
|
2574
|
-
.
|
|
2345
|
+
.union([
|
|
2346
|
+
z.enum(['name', 'description', 'readme']),
|
|
2347
|
+
z.array(z.enum(['name', 'description', 'readme'])),
|
|
2348
|
+
])
|
|
2575
2349
|
.optional()
|
|
2576
|
-
.describe('Search scope. "name" (repository names only), "description" (descriptions only), "readme" (README content).
|
|
2350
|
+
.describe('Search scope. "name" (repository names only), "description" (descriptions only), "readme" (README content). Can be single value or array.'),
|
|
2577
2351
|
// SORTING & LIMITS - Match CLI defaults exactly
|
|
2578
2352
|
sort: z
|
|
2579
2353
|
.enum([
|
|
@@ -2585,12 +2359,12 @@ function registerSearchGitHubReposTool(server) {
|
|
|
2585
2359
|
])
|
|
2586
2360
|
.optional()
|
|
2587
2361
|
.default('best-match')
|
|
2588
|
-
.describe('Sort criteria.
|
|
2362
|
+
.describe('Sort criteria.'),
|
|
2589
2363
|
order: z
|
|
2590
2364
|
.enum(['asc', 'desc'])
|
|
2591
2365
|
.optional()
|
|
2592
2366
|
.default('desc')
|
|
2593
|
-
.describe('Sort order direction.
|
|
2367
|
+
.describe('Sort order direction.'),
|
|
2594
2368
|
limit: z
|
|
2595
2369
|
.number()
|
|
2596
2370
|
.int()
|
|
@@ -2598,7 +2372,7 @@ function registerSearchGitHubReposTool(server) {
|
|
|
2598
2372
|
.max(100)
|
|
2599
2373
|
.optional()
|
|
2600
2374
|
.default(30)
|
|
2601
|
-
.describe('Maximum number of repositories to return (1-100).
|
|
2375
|
+
.describe('Maximum number of repositories to return (1-100).'),
|
|
2602
2376
|
},
|
|
2603
2377
|
annotations: {
|
|
2604
2378
|
title: 'GitHub Repository Search',
|
|
@@ -2828,8 +2602,258 @@ function buildGitHubReposSearchCommand(params) {
|
|
|
2828
2602
|
return { command: 'search', args };
|
|
2829
2603
|
}
|
|
2830
2604
|
|
|
2605
|
+
const GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME = 'githubViewRepoStructure';
|
|
2606
|
+
const DESCRIPTION$2 = `Browse GitHub repository file structure and navigate directories. Essential for exploring project layout, finding documentation, configuration files, and source code. Returns file/folder listings with size information. Automatically handles branch detection. Parameters: owner (required - GitHub username/org), repo (required - repository name), branch (required), path (optional - directory path).`;
|
|
2607
|
+
function registerViewRepositoryStructureTool(server) {
|
|
2608
|
+
server.registerTool(GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME, {
|
|
2609
|
+
description: DESCRIPTION$2,
|
|
2610
|
+
inputSchema: {
|
|
2611
|
+
owner: z
|
|
2612
|
+
.string()
|
|
2613
|
+
.min(1)
|
|
2614
|
+
.max(100)
|
|
2615
|
+
.regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/, 'Invalid GitHub username/org format')
|
|
2616
|
+
.describe('Repository owner/organization name (e.g., "facebook", "microsoft"). Do NOT include repository name.'),
|
|
2617
|
+
repo: z
|
|
2618
|
+
.string()
|
|
2619
|
+
.min(1)
|
|
2620
|
+
.max(100)
|
|
2621
|
+
.regex(/^[a-zA-Z0-9._-]+$/, 'Invalid repository name format')
|
|
2622
|
+
.describe(`Repository name under a organization. `),
|
|
2623
|
+
branch: z
|
|
2624
|
+
.string()
|
|
2625
|
+
.min(1)
|
|
2626
|
+
.max(255)
|
|
2627
|
+
.regex(/^[^\s]+$/, 'Branch name cannot contain spaces')
|
|
2628
|
+
.describe('Branch name (e.g., "main", "master", "develop"). Tool will automatically try default branch if specified branch is not found.'),
|
|
2629
|
+
path: z
|
|
2630
|
+
.string()
|
|
2631
|
+
.optional()
|
|
2632
|
+
.default('')
|
|
2633
|
+
.refine(path => !path.includes('..'), 'Path traversal not allowed')
|
|
2634
|
+
.refine(path => path.length <= 500, 'Path too long')
|
|
2635
|
+
.describe('Directory path within repository (e.g., "src", "docs", "src/components"). Leave empty for root directory. Do NOT start with slash.'),
|
|
2636
|
+
},
|
|
2637
|
+
annotations: {
|
|
2638
|
+
title: 'GitHub Repository Explorer',
|
|
2639
|
+
readOnlyHint: true,
|
|
2640
|
+
destructiveHint: false,
|
|
2641
|
+
idempotentHint: true,
|
|
2642
|
+
openWorldHint: true,
|
|
2643
|
+
},
|
|
2644
|
+
}, async (args) => {
|
|
2645
|
+
try {
|
|
2646
|
+
const result = await viewRepositoryStructure(args);
|
|
2647
|
+
return result;
|
|
2648
|
+
}
|
|
2649
|
+
catch (error) {
|
|
2650
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
2651
|
+
return createResult({
|
|
2652
|
+
error: `Failed to explore repository. ${errorMessage}. Verify repository exists and is accessible`,
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
});
|
|
2656
|
+
}
|
|
2657
|
+
/**
|
|
2658
|
+
* Views the structure of a GitHub repository at a specific path.
|
|
2659
|
+
* Optimized for code analysis workflows with smart defaults and clear errors.
|
|
2660
|
+
*/
|
|
2661
|
+
async function viewRepositoryStructure(params) {
|
|
2662
|
+
const cacheKey = generateCacheKey('gh-repo-structure', params);
|
|
2663
|
+
return withCache(cacheKey, async () => {
|
|
2664
|
+
const { owner, repo, branch, path = '' } = params;
|
|
2665
|
+
try {
|
|
2666
|
+
// Clean up path
|
|
2667
|
+
const cleanPath = path.startsWith('/') ? path.substring(1) : path;
|
|
2668
|
+
// Try the requested branch first, then fallback to main/master
|
|
2669
|
+
const branchesToTry = await getSmartBranchFallback(owner, repo, branch);
|
|
2670
|
+
let items = [];
|
|
2671
|
+
let usedBranch = branch;
|
|
2672
|
+
let lastError = null;
|
|
2673
|
+
let attemptCount = 0;
|
|
2674
|
+
const maxAttempts = branchesToTry.length;
|
|
2675
|
+
const triedBranches = [];
|
|
2676
|
+
for (const tryBranch of branchesToTry) {
|
|
2677
|
+
if (attemptCount >= maxAttempts)
|
|
2678
|
+
break;
|
|
2679
|
+
attemptCount++;
|
|
2680
|
+
triedBranches.push(tryBranch);
|
|
2681
|
+
try {
|
|
2682
|
+
const apiPath = `/repos/${owner}/${repo}/contents/${cleanPath}?ref=${tryBranch}`;
|
|
2683
|
+
const result = await executeGitHubCommand('api', [apiPath], {
|
|
2684
|
+
cache: false,
|
|
2685
|
+
});
|
|
2686
|
+
if (!result.isError) {
|
|
2687
|
+
const execResult = JSON.parse(result.content[0].text);
|
|
2688
|
+
const apiItems = execResult.result;
|
|
2689
|
+
items = Array.isArray(apiItems) ? apiItems : [apiItems];
|
|
2690
|
+
usedBranch = tryBranch;
|
|
2691
|
+
break;
|
|
2692
|
+
}
|
|
2693
|
+
else {
|
|
2694
|
+
lastError = new Error(result.content[0].text);
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
catch (error) {
|
|
2698
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2699
|
+
continue;
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
if (items.length === 0) {
|
|
2703
|
+
// Check repository existence only after content fetch fails
|
|
2704
|
+
const repoCheckResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
|
|
2705
|
+
cache: false,
|
|
2706
|
+
});
|
|
2707
|
+
if (repoCheckResult.isError) {
|
|
2708
|
+
const repoErrorMsg = repoCheckResult.content[0].text;
|
|
2709
|
+
if (repoErrorMsg.includes('404')) {
|
|
2710
|
+
return createResult({
|
|
2711
|
+
error: `Repository "${owner}/${repo}" not found. It might have been deleted, renamed, or made private. Use github_search_code to find current location.`,
|
|
2712
|
+
});
|
|
2713
|
+
}
|
|
2714
|
+
else if (repoErrorMsg.includes('403')) {
|
|
2715
|
+
return createResult({
|
|
2716
|
+
error: `Repository "${owner}/${repo}" exists but access is denied. Repository might be private or archived. Use api_status_check to verify permissions.`,
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
const errorMsg = lastError?.message || 'Unknown error';
|
|
2721
|
+
if (errorMsg.includes('404') || errorMsg.includes('Not Found')) {
|
|
2722
|
+
if (path) {
|
|
2723
|
+
const searchSuggestion = await suggestPathSearchFallback(owner, path);
|
|
2724
|
+
return createResult({
|
|
2725
|
+
error: `Path "${path}" not found in any branch (tried: ${triedBranches.join(', ')}).${searchSuggestion}`,
|
|
2726
|
+
});
|
|
2727
|
+
}
|
|
2728
|
+
else {
|
|
2729
|
+
return createResult({
|
|
2730
|
+
error: `Repository "${owner}/${repo}" structure not accessible. Repository might be empty, private, or you might not have sufficient permissions. Use github_search_code with owner="${owner}" to find accessible repositories.`,
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
else if (errorMsg.includes('403') || errorMsg.includes('Forbidden')) {
|
|
2735
|
+
return createResult({
|
|
2736
|
+
error: `Access denied to "${owner}/${repo}". Repository exists but might be private/archived. Use api_status_check to verify permissions, or github_search_code with owner="${owner}" to find accessible repositories.`,
|
|
2737
|
+
});
|
|
2738
|
+
}
|
|
2739
|
+
else {
|
|
2740
|
+
const searchSuggestion = path
|
|
2741
|
+
? await suggestPathSearchFallback(owner, path)
|
|
2742
|
+
: '';
|
|
2743
|
+
return createResult({
|
|
2744
|
+
error: `Failed to access "${owner}/${repo}": ${errorMsg}. Check network connection and repository permissions.${searchSuggestion}`,
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
// Limit total items to 100 for efficiency
|
|
2749
|
+
const limitedItems = items.slice(0, 100);
|
|
2750
|
+
// Sort: directories first, then alphabetically
|
|
2751
|
+
limitedItems.sort((a, b) => {
|
|
2752
|
+
if (a.type !== b.type) {
|
|
2753
|
+
return a.type === 'dir' ? -1 : 1;
|
|
2754
|
+
}
|
|
2755
|
+
return a.name.localeCompare(b.name);
|
|
2756
|
+
});
|
|
2757
|
+
// Create simplified, token-efficient structure
|
|
2758
|
+
const files = limitedItems
|
|
2759
|
+
.filter(item => item.type === 'file')
|
|
2760
|
+
.map(item => ({
|
|
2761
|
+
name: item.name,
|
|
2762
|
+
size: item.size,
|
|
2763
|
+
url: item.path, // Use path for fetching
|
|
2764
|
+
}));
|
|
2765
|
+
const folders = limitedItems
|
|
2766
|
+
.filter(item => item.type === 'dir')
|
|
2767
|
+
.map(item => ({
|
|
2768
|
+
name: item.name,
|
|
2769
|
+
url: item.path, // Use path for browsing
|
|
2770
|
+
}));
|
|
2771
|
+
return createResult({
|
|
2772
|
+
data: {
|
|
2773
|
+
repository: `${owner}/${repo}`,
|
|
2774
|
+
branch: usedBranch,
|
|
2775
|
+
path: cleanPath || '/',
|
|
2776
|
+
githubBasePath: `https://api.github.com/repos/${owner}/${repo}/contents/`,
|
|
2777
|
+
files: {
|
|
2778
|
+
count: files.length,
|
|
2779
|
+
files: files,
|
|
2780
|
+
},
|
|
2781
|
+
folders: {
|
|
2782
|
+
count: folders.length,
|
|
2783
|
+
folders: folders,
|
|
2784
|
+
},
|
|
2785
|
+
},
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2788
|
+
catch (error) {
|
|
2789
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2790
|
+
return createResult({
|
|
2791
|
+
error: `Failed to access repository "${owner}/${repo}": ${errorMessage}. Verify repository name, permissions, and network connection.`,
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
});
|
|
2795
|
+
}
|
|
2796
|
+
/**
|
|
2797
|
+
* Smart branch detection with automatic fallback to common branch names.
|
|
2798
|
+
* Now includes more comprehensive branch detection and better error handling.
|
|
2799
|
+
*/
|
|
2800
|
+
async function getSmartBranchFallback(owner, repo, requestedBranch) {
|
|
2801
|
+
const branches = new Set([requestedBranch]);
|
|
2802
|
+
try {
|
|
2803
|
+
// Try to get repository info to find default branch
|
|
2804
|
+
const repoInfoResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
|
|
2805
|
+
cache: false,
|
|
2806
|
+
});
|
|
2807
|
+
if (!repoInfoResult.isError) {
|
|
2808
|
+
const execResult = JSON.parse(repoInfoResult.content[0].text);
|
|
2809
|
+
const repoData = execResult.result;
|
|
2810
|
+
const defaultBranch = repoData.default_branch;
|
|
2811
|
+
if (defaultBranch) {
|
|
2812
|
+
branches.add(defaultBranch);
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
catch {
|
|
2817
|
+
// If we can't get repo info, proceed with standard fallbacks
|
|
2818
|
+
}
|
|
2819
|
+
// Add only main/master as fallback branches
|
|
2820
|
+
const commonBranches = ['main', 'master'];
|
|
2821
|
+
commonBranches.forEach(branch => branches.add(branch));
|
|
2822
|
+
// Convert Set back to array, with requested branch first
|
|
2823
|
+
const branchesArray = Array.from(branches);
|
|
2824
|
+
branchesArray.sort((a, b) => {
|
|
2825
|
+
if (a === requestedBranch)
|
|
2826
|
+
return -1;
|
|
2827
|
+
if (b === requestedBranch)
|
|
2828
|
+
return 1;
|
|
2829
|
+
return 0;
|
|
2830
|
+
});
|
|
2831
|
+
return branchesArray;
|
|
2832
|
+
}
|
|
2833
|
+
// Helper function to suggest path search strategy
|
|
2834
|
+
async function suggestPathSearchFallback(owner, path) {
|
|
2835
|
+
try {
|
|
2836
|
+
// Extract last path segment and try to find in same organization
|
|
2837
|
+
const pathSegment = path.split('/').pop() || path;
|
|
2838
|
+
const searchResult = await executeGitHubCommand('api', [
|
|
2839
|
+
`/search/code?q=${encodeURIComponent(pathSegment)}+in:path+org:${owner}`,
|
|
2840
|
+
], { cache: false });
|
|
2841
|
+
if (!searchResult.isError) {
|
|
2842
|
+
const results = JSON.parse(searchResult.content[0].text);
|
|
2843
|
+
if (results.total_count > 0) {
|
|
2844
|
+
const firstMatch = results.items[0];
|
|
2845
|
+
return ` Directory might be in ${firstMatch.repository.full_name}. Try these searches:\n1. github_search_code with query="${pathSegment}" owner="${owner}"\n2. github_search_code with query="path:${path}" owner="${owner}"`;
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
catch {
|
|
2850
|
+
// Fallback to generic message if search fails
|
|
2851
|
+
}
|
|
2852
|
+
return ` Try these searches:\n1. github_search_code with query="${path.split('/').pop()}" owner="${owner}"\n2. github_search_code with query="path:${path}"`;
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2831
2855
|
const NPM_PACKAGE_SEARCH_TOOL_NAME = 'npmPackageSearch';
|
|
2832
|
-
const DESCRIPTION$1 = `Search NPM packages
|
|
2856
|
+
const DESCRIPTION$1 = `Search NPM packages by name or functionality keywords. Supports multiple search terms for comprehensive package discovery. Returns package name, version, description, keywords, and repository information. Best for finding JavaScript/TypeScript libraries and tools.`;
|
|
2833
2857
|
const MAX_DESCRIPTION_LENGTH = 100;
|
|
2834
2858
|
const MAX_KEYWORDS = 10;
|
|
2835
2859
|
function registerNpmSearchTool(server) {
|
|
@@ -2838,7 +2862,7 @@ function registerNpmSearchTool(server) {
|
|
|
2838
2862
|
inputSchema: {
|
|
2839
2863
|
queries: z
|
|
2840
2864
|
.union([z.string(), z.array(z.string())])
|
|
2841
|
-
.describe('Search terms for packages.
|
|
2865
|
+
.describe('Search terms for NPM packages (e.g., "react hooks", ["typescript", "eslint"], "data visualization"). Use functionality keywords rather than exact package names for best results.'),
|
|
2842
2866
|
searchLimit: z
|
|
2843
2867
|
.number()
|
|
2844
2868
|
.int()
|
|
@@ -2937,7 +2961,7 @@ function parseNpmSearchOutput(output) {
|
|
|
2937
2961
|
}
|
|
2938
2962
|
|
|
2939
2963
|
const NPM_VIEW_PACKAGE_TOOL_NAME = 'npmViewPackage';
|
|
2940
|
-
const DESCRIPTION = `
|
|
2964
|
+
const DESCRIPTION = `Get detailed NPM package information including version, description, repository URL, size, and download statistics. Essential for understanding package details before installation. Returns optimized metadata for package evaluation and code navigation.`;
|
|
2941
2965
|
function registerNpmViewPackageTool(server) {
|
|
2942
2966
|
server.registerTool(NPM_VIEW_PACKAGE_TOOL_NAME, {
|
|
2943
2967
|
description: DESCRIPTION,
|
|
@@ -2945,7 +2969,7 @@ function registerNpmViewPackageTool(server) {
|
|
|
2945
2969
|
packageName: z
|
|
2946
2970
|
.string()
|
|
2947
2971
|
.min(1)
|
|
2948
|
-
.describe('NPM package name (e.g., "react", "express", "@types/node")'),
|
|
2972
|
+
.describe('NPM package name (e.g., "react", "express", "@types/node"). Include @ prefix for scoped packages.'),
|
|
2949
2973
|
},
|
|
2950
2974
|
annotations: {
|
|
2951
2975
|
title: 'NPM Package Analyzer',
|
|
@@ -3095,133 +3119,125 @@ using gh cli for github and npm cli for packages.
|
|
|
3095
3119
|
|
|
3096
3120
|
CRITICAL SEARCH PRINCIPLES:
|
|
3097
3121
|
|
|
3098
|
-
|
|
3099
|
-
a) START BROAD: Begin with simple, general terms (1-2 words max)
|
|
3100
|
-
b) ANALYZE RESULTS: Learn from what you find
|
|
3101
|
-
c) REFINE GRADUALLY: Add filters only after understanding the landscape
|
|
3122
|
+
PROGRESSIVE SEARCH STRATEGY (MOST IMPORTANT):
|
|
3123
|
+
a) START BROAD: Begin with simple, general queries and terms (1-2 words max)
|
|
3124
|
+
b) ANALYZE RESULTS: Learn from what you find
|
|
3125
|
+
c) REFINE GRADUALLY: Add filters only after understanding the landscape and if needed (only if the user asks for it explicitly)
|
|
3102
3126
|
d) MULTIPLE ANGLES: Try different search terms if first approach yields no results
|
|
3103
3127
|
|
|
3104
|
-
|
|
3105
|
-
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
3rd search: owner:facebook with specific terms (TARGETED)
|
|
3109
|
-
|
|
3110
|
-
- User asks about "authentication libraries"
|
|
3111
|
-
1st search: "auth" or "authentication" (BROAD)
|
|
3112
|
-
2nd search: Add language filter based on results
|
|
3113
|
-
3rd search: Focus on specific owners/topics found
|
|
3114
|
-
|
|
3115
|
-
## 3. HANDLING NO RESULTS:
|
|
3116
|
-
- NEVER give up after one failed search
|
|
3117
|
-
- Try progressively BROADER terms
|
|
3118
|
-
- Remove ALL filters and try core keywords
|
|
3119
|
-
- Search for related concepts (e.g., "auth" → "login" → "session")
|
|
3120
|
-
- Check different owners/organizations
|
|
3121
|
-
- For code search: try searching in popular repos first
|
|
3128
|
+
HANDLING NO RESULTS:
|
|
3129
|
+
- NEVER give up after one failed search.
|
|
3130
|
+
- Act on fallbacks messages and try to understand the user's intent.
|
|
3131
|
+
- Try progressively BROADER terms (simplify queries and remove filters)
|
|
3122
3132
|
|
|
3123
|
-
|
|
3133
|
+
SMART FILTER USAGE:
|
|
3124
3134
|
- NO FILTERS on first search (unless user specifies)
|
|
3125
3135
|
- Add ONE filter at a time based on results
|
|
3126
|
-
- Common progression: query → +language → +stars → +owner
|
|
3136
|
+
- Common progression: query → +language → +stars → +owner (only if user asks for it explicitly)
|
|
3127
3137
|
- Reserve complex filters for final refinement
|
|
3128
3138
|
|
|
3129
|
-
|
|
3130
|
-
- Use repository search to find relevant repos FIRST
|
|
3131
|
-
- Then use code search within those repos
|
|
3132
|
-
- Check npm packages for JavaScript/TypeScript queries
|
|
3133
|
-
- Verify access with api_status_check for private repos
|
|
3134
|
-
|
|
3135
|
-
## 6. RESEARCH BEST PRACTICES:
|
|
3139
|
+
RESEARCH BEST PRACTICES:
|
|
3136
3140
|
- Conduct COMPREHENSIVE research with multiple searches
|
|
3137
3141
|
- Learn from each search to improve the next
|
|
3138
3142
|
- Provide context about your search strategy
|
|
3139
3143
|
- Always verify technical details with actual code
|
|
3140
3144
|
|
|
3141
|
-
|
|
3145
|
+
COMPLEX ANALYSIS PATTERNS:
|
|
3142
3146
|
|
|
3143
|
-
|
|
3144
|
-
1. Search repos separately: "
|
|
3145
|
-
2.
|
|
3146
|
-
3.
|
|
3147
|
+
Multi-terms Comparison
|
|
3148
|
+
1. Search repos separately: "repoA", then "repoB"
|
|
3149
|
+
2. Review repositories structure and files
|
|
3150
|
+
3. Review code and documentation
|
|
3147
3151
|
4. Compare similar functionalities across repos
|
|
3152
|
+
5. Use filters and flags to narrow down the results
|
|
3148
3153
|
|
|
3149
|
-
|
|
3150
|
-
1. Start broad: "state" or "store"
|
|
3151
|
-
2. Identify major libraries: Redux, Zustand, Jotai
|
|
3152
|
-
3. Search implementation patterns: "reducer", "atom", "selector"
|
|
3153
|
-
4. Analyze each approach separately, then compare
|
|
3154
|
-
|
|
3155
|
-
### Evolution Tracking (e.g., Feature History):
|
|
3154
|
+
Evolution Tracking (e.g., Feature History):
|
|
3156
3155
|
1. Use commit search with broad terms first
|
|
3157
3156
|
2. Narrow by date ranges progressively
|
|
3158
3157
|
3. Track changes in specific files over time
|
|
3159
3158
|
4. Identify key contributors and their patterns
|
|
3160
3159
|
|
|
3161
|
-
|
|
3162
|
-
1. Search for benchmarks: "benchmark", "perf", "performance"
|
|
3163
|
-
2. Look for optimization commits: "optimize", "faster", "improve"
|
|
3164
|
-
3. Find profiling code: "profile", "measure", "timing"
|
|
3165
|
-
4. Compare implementation strategies
|
|
3166
|
-
|
|
3167
|
-
## 8. CROSS-REPOSITORY INTELLIGENCE:
|
|
3160
|
+
CROSS-REPOSITORY INTELLIGENCE:
|
|
3168
3161
|
- When comparing frameworks, search EACH separately first
|
|
3169
3162
|
- Build mental model of each codebase structure
|
|
3170
|
-
- Use discovered patterns to refine searches
|
|
3171
|
-
|
|
3163
|
+
- Use discovered patterns to refine searches and connect findings across repositories for insights
|
|
3164
|
+
|
|
3165
|
+
RESEARCH WORKFLOW BEST PRACTICES:
|
|
3166
|
+
|
|
3167
|
+
Discovery Phase (Unknown Projects/Topics):
|
|
3168
|
+
- START with ${GITHUB_SEARCH_REPOSITORIES_TOOL_NAME} using TOPICS
|
|
3169
|
+
- Topics are underutilized but extremely powerful for discovery
|
|
3170
|
+
- Example: { topic: ["react", "typescript", "testing"], stars: ">500" }
|
|
3171
|
+
|
|
3172
|
+
Understanding Phase
|
|
3173
|
+
- ALWAYS use ${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME} first to verify repository exists
|
|
3174
|
+
- CRITICAL: Many failures are due to incorrect repository names - verify before proceeding
|
|
3175
|
+
- Check README.md, docs/, configuration files
|
|
3176
|
+
- Understand project structure before searching code
|
|
3177
|
+
- Never make assumptions about repository names or file locations - always verify
|
|
3178
|
+
|
|
3179
|
+
Implementation Search (Specific Code):
|
|
3180
|
+
- Fetch importnat files using ${GITHUB_GET_FILE_CONTENT_TOOL_NAME}
|
|
3181
|
+
- Use ${GITHUB_SEARCH_CODE_TOOL_NAME} with NARROW queries
|
|
3182
|
+
- Start precise, broaden only if needed
|
|
3183
|
+
- Use exact phrases when you know patterns
|
|
3184
|
+
- Verify dependences and imports
|
|
3185
|
+
|
|
3186
|
+
File Access Validation Workflow:
|
|
3187
|
+
1. VERIFY repository name first with ${GITHUB_SEARCH_CODE_TOOL_NAME} if unsure
|
|
3188
|
+
2. Use ${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME} to confirm repository structure
|
|
3189
|
+
3. Navigate to parent directory first to understand layout
|
|
3190
|
+
4. Only then use ${GITHUB_GET_FILE_CONTENT_TOOL_NAME} with confirmed paths
|
|
3191
|
+
5. Most "file not found" errors are due to incorrect repository names
|
|
3172
3192
|
|
|
3173
|
-
|
|
3193
|
+
TOOL-SPECIFIC BEST PRACTICES:
|
|
3174
3194
|
|
|
3175
|
-
|
|
3195
|
+
${API_STATUS_CHECK_TOOL_NAME}:
|
|
3176
3196
|
- Run FIRST when dealing with private repositories
|
|
3177
3197
|
- Use organizations list to scope searches
|
|
3178
3198
|
- Verify authentication before extensive searches
|
|
3179
3199
|
|
|
3180
|
-
|
|
3200
|
+
${GITHUB_SEARCH_REPOSITORIES_TOOL_NAME}:
|
|
3181
3201
|
- Start with topic/language, add stars/forks filters later
|
|
3182
3202
|
- Use date ranges for trending analysis
|
|
3183
3203
|
- Combine multiple searches for comprehensive discovery
|
|
3184
3204
|
|
|
3185
|
-
|
|
3205
|
+
${GITHUB_SEARCH_CODE_TOOL_NAME}:
|
|
3186
3206
|
- Begin with function/class names, not full signatures
|
|
3187
3207
|
- Use extension filters for targeted searches
|
|
3188
3208
|
- Try partial matches before exact phrases
|
|
3189
3209
|
|
|
3190
|
-
|
|
3210
|
+
${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME}:
|
|
3191
3211
|
- Navigate from root, then drill down
|
|
3192
3212
|
- Use for understanding project organization
|
|
3193
3213
|
- Check common paths: src/, lib/, packages/
|
|
3194
3214
|
|
|
3195
|
-
|
|
3215
|
+
${GITHUB_GET_FILE_CONTENT_TOOL_NAME}:
|
|
3196
3216
|
- Verify file paths with repo structure first
|
|
3197
3217
|
- Use for implementation details and documentation
|
|
3198
|
-
- Remember 300KB limit for large files
|
|
3199
3218
|
|
|
3200
|
-
|
|
3201
|
-
- Search by feature keywords
|
|
3202
|
-
- Use author filter for contributor analysis
|
|
3219
|
+
${GITHUB_SEARCH_COMMITS_TOOL_NAME}:
|
|
3220
|
+
- Search by feature keywords
|
|
3203
3221
|
- Date ranges help track feature evolution
|
|
3204
3222
|
|
|
3205
|
-
|
|
3206
|
-
- Search for problem descriptions
|
|
3223
|
+
${GITHUB_SEARCH_ISSUES_TOOL_NAME} & ${GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME}:
|
|
3224
|
+
- Search for problem descriptions
|
|
3207
3225
|
- Use state filters progressively
|
|
3208
|
-
- Labels reveal project categorization
|
|
3209
3226
|
|
|
3210
|
-
|
|
3211
|
-
-
|
|
3212
|
-
- Search multiple related terms in parallel
|
|
3213
|
-
- Aggregate results for comprehensive view
|
|
3227
|
+
${NPM_PACKAGE_SEARCH_TOOL_NAME}:
|
|
3228
|
+
- Search npm package in registry - use one term at a time (partial or exact)
|
|
3214
3229
|
|
|
3215
|
-
|
|
3216
|
-
-
|
|
3217
|
-
- Review exports for API understanding
|
|
3218
|
-
- Use version history to gauge stability
|
|
3230
|
+
${NPM_VIEW_PACKAGE_TOOL_NAME}:
|
|
3231
|
+
- Returns essential data like github path and package metadata like dependencies, exports...
|
|
3219
3232
|
|
|
3220
|
-
|
|
3233
|
+
CHAIN OF THOUGHT OPTIMIZATION:
|
|
3221
3234
|
- Plan search sequence before executing
|
|
3222
3235
|
- Document reasoning for each search refinement
|
|
3223
3236
|
- Build knowledge progressively, don't jump to specifics
|
|
3224
3237
|
- Validate findings with multiple sources
|
|
3238
|
+
- Ask user for clarification if needed
|
|
3239
|
+
- Learn from results and refine search strategy
|
|
3240
|
+
- Output high level summary of the search and results based on data and not assumptions
|
|
3225
3241
|
`;
|
|
3226
3242
|
|
|
3227
3243
|
const SERVER_CONFIG = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "octocode-mcp",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.8",
|
|
4
4
|
"description": "Model Context Protocol (MCP) server for advanced GitHub repository analysis, code discovery, and npm package exploration. Provides AI assistants with powerful tools to search, analyze, and understand codebases across GitHub and npm ecosystems.",
|
|
5
5
|
"author": "Guy Bary <guybary@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/bgauryy/octocode-mcp#readme",
|