octocode-mcp 2.3.7 → 2.3.9

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.
Files changed (2) hide show
  1. package/build/index.js +1502 -696
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -168,11 +168,13 @@ function escapeWindowsCmdArg(arg) {
168
168
  * Preserves AND search logic by not over-escaping space-separated terms
169
169
  */
170
170
  function escapeUnixShellArg(arg, isGitHubQuery) {
171
- // For GitHub search queries, we need to preserve AND logic
171
+ // For GitHub search queries, we need to preserve AND logic and quoted phrases
172
172
  if (isGitHubQuery) {
173
- // If the query contains quotes, preserve them for exact phrase matching
173
+ // If the query contains quotes, we need to preserve them for GitHub CLI
174
+ // but escape the entire argument for the shell
174
175
  if (arg.includes('"')) {
175
176
  // Use single quotes to wrap the entire query while preserving internal quotes
177
+ // This allows GitHub CLI to see: "quoted phrase" other terms
176
178
  return `'${arg.replace(/'/g, "'\"'\"'")}'`;
177
179
  }
178
180
  // For space-separated terms (AND search), only escape if absolutely necessary
@@ -230,18 +232,30 @@ async function executeGitHubCommand(command, args = [], options = {}) {
230
232
  // Get shell configuration
231
233
  const shellConfig = getShellConfig(options.windowsShell);
232
234
  // Build command with validated prefix and properly escaped arguments
233
- // For GitHub search commands, qualifiers like "language:typescript" should not be escaped
234
- // Only the main query term (if it contains spaces) needs escaping
235
+ // For GitHub search commands, we need to distinguish between:
236
+ // 1. Main query (index 1) - needs special escaping for AND logic
237
+ // 2. CLI flags (--flag=value) - standard escaping
238
+ // 3. Search qualifiers (key:value) - minimal escaping
235
239
  const escapedArgs = args.map((arg, index) => {
236
240
  const isMainQueryArgument = command === 'search' && index === 1;
241
+ const isCliFlag = arg.startsWith('--');
237
242
  const isGitHubQualifier = command === 'search' &&
238
243
  index > 1 &&
239
- (arg.includes(':') || arg.startsWith('(')) &&
240
- !arg.startsWith('--');
241
- // Don't escape GitHub search qualifiers - they need to be passed as-is
242
- // This includes qualifiers like "language:typescript", "user:microsoft", "org:microsoft"
243
- // and complex expressions like "(user:microsoft OR org:microsoft)"
244
+ !isCliFlag &&
245
+ (arg.includes(':') || arg.startsWith('('));
246
+ // CLI flags like --language=javascript, --repo=owner/repo need standard escaping
247
+ if (isCliFlag) {
248
+ return escapeShellArg(arg, shellConfig.type, false);
249
+ }
250
+ // GitHub search qualifiers need special handling
251
+ // Most qualifiers can be passed as-is, but those with shell metacharacters need escaping
244
252
  if (isGitHubQualifier) {
253
+ // Check if the qualifier contains shell metacharacters that need escaping
254
+ if (/[<>&|;`$\\]/.test(arg)) {
255
+ // Escape qualifiers that contain shell metacharacters like size:<1000, size:>500
256
+ return escapeShellArg(arg, shellConfig.type, false);
257
+ }
258
+ // Safe qualifiers like "language:typescript", "user:microsoft" can be passed as-is
245
259
  return arg;
246
260
  }
247
261
  return escapeShellArg(arg, shellConfig.type, isMainQueryArgument);
@@ -303,6 +317,14 @@ async function executeCommand(fullCommand, type, options = {}, shellConfig) {
303
317
  stderr.trim() !== '';
304
318
  if (shouldTreatAsError) {
305
319
  const errorType = type === 'npm' ? 'NPM command error' : 'GitHub CLI command error';
320
+ // Enhanced error messaging for common GitHub issues
321
+ if (type === 'github' && stderr.includes('404')) {
322
+ const isRepoNotFound = stderr.includes('Not Found');
323
+ const enhancedMessage = isRepoNotFound
324
+ ? `${stderr}\n\nThis is often due to incorrect repository name. Use github_search_code to find the correct repository.`
325
+ : stderr;
326
+ return createErrorResult(errorType, new Error(enhancedMessage));
327
+ }
306
328
  return createErrorResult(errorType, new Error(stderr));
307
329
  }
308
330
  // Try to parse stdout as JSON, fallback to string if not possible
@@ -445,24 +467,16 @@ const ERROR_MESSAGES = {
445
467
  ISSUE_SEARCH_FAILED: 'Issue search failed - check auth or simplify query',
446
468
  PR_SEARCH_FAILED: 'PR search failed - check access and query syntax',
447
469
  PACKAGE_SEARCH_FAILED: 'Package search failed - try different keywords',
448
- // GitHub CLI errors
449
- CLI_INVALID_RESPONSE: 'GitHub CLI invalid response - check "gh version" and update',
450
- // Timeout errors
451
- SEARCH_TIMEOUT: 'Search timed out - add filters (language, owner) or use specific terms',
452
470
  // Query validation errors
453
471
  QUERY_TOO_LONG: 'Query too long (max 256 chars) - simplify search terms',
454
472
  QUERY_REQUIRED: 'Query required - provide search keywords',
455
- EMPTY_QUERY: 'Empty query - try "useState", "authentication", or language:python',
456
- QUERY_TOO_LONG_1000: 'Query too long (max 1000 chars) - use key terms like "error handling"',
457
- REPO_OR_OWNER_NOT_FOUND: 'Repository/owner not found - check spelling, visibility, and permissions',
458
- // Query syntax errors
459
- INVALID_QUERY_SYNTAX: 'Invalid syntax - Boolean operators not supported, use quotes for phrases',
460
- // Size/format validation errors
461
- INVALID_SIZE_FORMAT: 'Invalid size format - use >N, <N, or N..M without quotes',
462
- INVALID_SEARCH_SCOPE: 'Invalid scope - use "file" for content, "path" for filenames',
463
473
  // API Status check errors
464
474
  API_STATUS_CHECK_FAILED: 'API Status Check Failed',
465
- };
475
+ // NPM Errors
476
+ NPM_PACKAGE_NOT_FOUND: 'Package not found on NPM registry',
477
+ NPM_CONNECTION_FAILED: 'NPM registry connection failed',
478
+ NPM_CLI_ERROR: 'NPM CLI issue detected',
479
+ NPM_PERMISSION_ERROR: 'NPM permission issue'};
466
480
  const SUGGESTIONS = {
467
481
  // Code search suggestions
468
482
  CODE_SEARCH_NO_RESULTS: `No results found. Try this progression:
@@ -486,6 +500,31 @@ const SUGGESTIONS = {
486
500
  2. Break compound words: "authlib" → "auth"
487
501
  3. Search by use case: "user login" vs "authentication"
488
502
  4. Try category terms: "framework", "tool", "library"`,
503
+ // File Content Suggestions
504
+ FILE_NOT_FOUND_RECOVERY: `Quick fixes:
505
+ • Use github_view_repo_structure to verify path exists
506
+ • Check for typos in file path
507
+ • Try different branch (main/master/develop)`,
508
+ FILE_TOO_LARGE_RECOVERY: `Alternative strategies:
509
+ • Use github_search_code to search within the file
510
+ • Download directly from GitHub
511
+ • Use github_view_repo_structure to find smaller related files
512
+ • Look for configuration or summary files instead`,
513
+ // Repository Suggestions
514
+ REPOSITORY_NOT_FOUND_RECOVERY: `This is often due to incorrect repository name. Steps to resolve:
515
+ 1. Use github_search_repositories to find the correct repository
516
+ 2. Verify the exact repository name
517
+ 3. Check if the repository might have been renamed or moved`,
518
+ // NPM Suggestions
519
+ NPM_DISCOVERY_STRATEGIES: `Discovery strategies:
520
+ • Functional search: "validation", "testing", "charts"
521
+ • Ecosystem search: "react", "typescript", "node"
522
+ • Use github_search_repositories for related projects`,
523
+ NPM_PACKAGE_NAME_ALTERNATIVES: `Try these alternatives:
524
+ • Try with dashes instead of underscores
525
+ • Try without dashes
526
+ • Try scoped package format
527
+ • Use npm_package_search for discovery`,
489
528
  };
490
529
  // Helper function to get error message with context-specific suggestions
491
530
  function getErrorWithSuggestion(options) {
@@ -543,9 +582,348 @@ function createSearchFailedError(type = 'code') {
543
582
  return ERROR_MESSAGES.SEARCH_FAILED;
544
583
  }
545
584
  }
585
+ function createNpmPackageNotFoundError(packageName) {
586
+ const suggestions = [];
587
+ if (packageName.includes('_')) {
588
+ suggestions.push(`• Try with dashes: "${packageName.replace(/_/g, '-')}"`);
589
+ }
590
+ if (packageName.includes('-')) {
591
+ suggestions.push(`• Try without dashes: "${packageName.replace(/-/g, '')}"`);
592
+ }
593
+ if (!packageName.includes('/') && packageName.length > 2) {
594
+ suggestions.push(`• Try scoped format: "@${packageName.slice(0, 3)}/${packageName}"`);
595
+ }
596
+ return `${ERROR_MESSAGES.NPM_PACKAGE_NOT_FOUND}: "${packageName}"
597
+
598
+ ${suggestions.length > 0 ? suggestions.join('\n') + '\n\n' : ''}${SUGGESTIONS.NPM_PACKAGE_NAME_ALTERNATIVES}
599
+
600
+ ${SUGGESTIONS.NPM_DISCOVERY_STRATEGIES}`;
601
+ }
602
+
603
+ /**
604
+ * Tool relationship map for standardized cross-tool suggestions
605
+ * Defines how tools connect and when to suggest alternatives
606
+ */
607
+ const TOOL_NAMES = {
608
+ API_STATUS_CHECK: 'api_status_check',
609
+ GITHUB_FETCH_CONTENT: 'github_fetch_content',
610
+ GITHUB_SEARCH_CODE: 'github_search_code',
611
+ GITHUB_SEARCH_COMMITS: 'github_search_commits',
612
+ GITHUB_SEARCH_ISSUES: 'github_search_issues',
613
+ GITHUB_SEARCH_PULL_REQUESTS: 'github_search_pull_requests',
614
+ GITHUB_SEARCH_REPOSITORIES: 'github_search_repositories',
615
+ GITHUB_VIEW_REPO_STRUCTURE: 'github_view_repo_structure',
616
+ NPM_PACKAGE_SEARCH: 'npm_package_search',
617
+ NPM_VIEW_PACKAGE: 'npm_view_package',
618
+ };
619
+ const TOOL_RELATIONSHIPS = {
620
+ [TOOL_NAMES.NPM_PACKAGE_SEARCH]: {
621
+ fallbackTools: [
622
+ {
623
+ tool: TOOL_NAMES.GITHUB_SEARCH_REPOSITORIES,
624
+ reason: 'to find repositories by topic or language',
625
+ },
626
+ {
627
+ tool: TOOL_NAMES.GITHUB_SEARCH_CODE,
628
+ reason: 'to search for package usage examples',
629
+ },
630
+ ],
631
+ nextSteps: [
632
+ {
633
+ tool: TOOL_NAMES.NPM_VIEW_PACKAGE,
634
+ reason: 'to get detailed package information and repository URL',
635
+ },
636
+ {
637
+ tool: TOOL_NAMES.GITHUB_VIEW_REPO_STRUCTURE,
638
+ reason: 'to explore the package source code structure',
639
+ },
640
+ ],
641
+ },
642
+ [TOOL_NAMES.NPM_VIEW_PACKAGE]: {
643
+ fallbackTools: [
644
+ {
645
+ tool: TOOL_NAMES.NPM_PACKAGE_SEARCH,
646
+ reason: 'to discover similar packages',
647
+ },
648
+ {
649
+ tool: TOOL_NAMES.GITHUB_SEARCH_REPOSITORIES,
650
+ reason: 'to find the repository directly',
651
+ },
652
+ ],
653
+ nextSteps: [
654
+ {
655
+ tool: TOOL_NAMES.GITHUB_VIEW_REPO_STRUCTURE,
656
+ reason: 'to explore repository structure',
657
+ },
658
+ {
659
+ tool: TOOL_NAMES.GITHUB_FETCH_CONTENT,
660
+ reason: 'to read specific files like README or package.json',
661
+ },
662
+ {
663
+ tool: TOOL_NAMES.GITHUB_SEARCH_CODE,
664
+ reason: 'to find usage examples',
665
+ },
666
+ ],
667
+ },
668
+ [TOOL_NAMES.GITHUB_SEARCH_REPOSITORIES]: {
669
+ fallbackTools: [
670
+ {
671
+ tool: TOOL_NAMES.NPM_PACKAGE_SEARCH,
672
+ reason: 'if searching for packages or libraries',
673
+ },
674
+ {
675
+ tool: TOOL_NAMES.GITHUB_SEARCH_CODE,
676
+ reason: 'to search within repository contents',
677
+ },
678
+ ],
679
+ nextSteps: [
680
+ {
681
+ tool: TOOL_NAMES.GITHUB_VIEW_REPO_STRUCTURE,
682
+ reason: 'to explore repository contents',
683
+ },
684
+ {
685
+ tool: TOOL_NAMES.GITHUB_SEARCH_ISSUES,
686
+ reason: 'to check open issues and discussions',
687
+ },
688
+ {
689
+ tool: TOOL_NAMES.NPM_VIEW_PACKAGE,
690
+ reason: 'if repository is an NPM package',
691
+ },
692
+ ],
693
+ },
694
+ [TOOL_NAMES.GITHUB_SEARCH_CODE]: {
695
+ fallbackTools: [
696
+ {
697
+ tool: TOOL_NAMES.NPM_PACKAGE_SEARCH,
698
+ reason: 'if searching for package implementations',
699
+ },
700
+ {
701
+ tool: TOOL_NAMES.GITHUB_SEARCH_REPOSITORIES,
702
+ reason: 'to find repositories by topic',
703
+ },
704
+ {
705
+ tool: TOOL_NAMES.API_STATUS_CHECK,
706
+ reason: 'if no results (might be private repos)',
707
+ condition: 'no_results',
708
+ },
709
+ ],
710
+ nextSteps: [
711
+ {
712
+ tool: TOOL_NAMES.GITHUB_FETCH_CONTENT,
713
+ reason: 'to view full file contents',
714
+ },
715
+ {
716
+ tool: TOOL_NAMES.GITHUB_VIEW_REPO_STRUCTURE,
717
+ reason: 'to explore repository structure',
718
+ },
719
+ ],
720
+ prerequisites: [
721
+ {
722
+ tool: TOOL_NAMES.GITHUB_VIEW_REPO_STRUCTURE,
723
+ reason: 'to verify file paths before searching',
724
+ },
725
+ ],
726
+ },
727
+ [TOOL_NAMES.GITHUB_FETCH_CONTENT]: {
728
+ fallbackTools: [
729
+ {
730
+ tool: TOOL_NAMES.GITHUB_VIEW_REPO_STRUCTURE,
731
+ reason: 'to verify correct file path',
732
+ },
733
+ {
734
+ tool: TOOL_NAMES.GITHUB_SEARCH_CODE,
735
+ reason: 'to search for similar files',
736
+ },
737
+ ],
738
+ nextSteps: [
739
+ {
740
+ tool: TOOL_NAMES.GITHUB_VIEW_REPO_STRUCTURE,
741
+ reason: 'to explore related files',
742
+ },
743
+ {
744
+ tool: TOOL_NAMES.GITHUB_SEARCH_CODE,
745
+ reason: 'to find usage examples',
746
+ },
747
+ ],
748
+ prerequisites: [
749
+ {
750
+ tool: TOOL_NAMES.GITHUB_VIEW_REPO_STRUCTURE,
751
+ reason: 'to verify file exists and get correct path',
752
+ },
753
+ ],
754
+ },
755
+ [TOOL_NAMES.GITHUB_VIEW_REPO_STRUCTURE]: {
756
+ fallbackTools: [
757
+ {
758
+ tool: TOOL_NAMES.GITHUB_SEARCH_REPOSITORIES,
759
+ reason: 'to find the correct repository',
760
+ },
761
+ {
762
+ tool: TOOL_NAMES.API_STATUS_CHECK,
763
+ reason: 'if access denied (check authentication)',
764
+ condition: 'access_denied',
765
+ },
766
+ ],
767
+ nextSteps: [
768
+ {
769
+ tool: TOOL_NAMES.GITHUB_FETCH_CONTENT,
770
+ reason: 'to read specific files',
771
+ },
772
+ {
773
+ tool: TOOL_NAMES.GITHUB_SEARCH_CODE,
774
+ reason: 'to search within the repository',
775
+ },
776
+ ],
777
+ },
778
+ [TOOL_NAMES.GITHUB_SEARCH_COMMITS]: {
779
+ fallbackTools: [
780
+ {
781
+ tool: TOOL_NAMES.GITHUB_SEARCH_REPOSITORIES,
782
+ reason: 'to find repositories first',
783
+ },
784
+ {
785
+ tool: TOOL_NAMES.GITHUB_SEARCH_ISSUES,
786
+ reason: 'to find related discussions',
787
+ },
788
+ ],
789
+ nextSteps: [
790
+ {
791
+ tool: TOOL_NAMES.GITHUB_FETCH_CONTENT,
792
+ reason: 'to view files changed in commits',
793
+ },
794
+ {
795
+ tool: TOOL_NAMES.GITHUB_SEARCH_CODE,
796
+ reason: 'to find current implementation',
797
+ },
798
+ ],
799
+ },
800
+ [TOOL_NAMES.GITHUB_SEARCH_ISSUES]: {
801
+ fallbackTools: [
802
+ {
803
+ tool: TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS,
804
+ reason: 'to find related PRs',
805
+ },
806
+ {
807
+ tool: TOOL_NAMES.GITHUB_SEARCH_REPOSITORIES,
808
+ reason: 'to find the repository first',
809
+ },
810
+ ],
811
+ nextSteps: [
812
+ {
813
+ tool: TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS,
814
+ reason: 'to find solutions or implementations',
815
+ },
816
+ {
817
+ tool: TOOL_NAMES.GITHUB_SEARCH_CODE,
818
+ reason: 'to find related code',
819
+ },
820
+ ],
821
+ },
822
+ [TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS]: {
823
+ fallbackTools: [
824
+ {
825
+ tool: TOOL_NAMES.GITHUB_SEARCH_ISSUES,
826
+ reason: 'to find related issues',
827
+ },
828
+ {
829
+ tool: TOOL_NAMES.GITHUB_SEARCH_COMMITS,
830
+ reason: 'to find related commits',
831
+ },
832
+ ],
833
+ nextSteps: [
834
+ {
835
+ tool: TOOL_NAMES.GITHUB_FETCH_CONTENT,
836
+ reason: 'to view PR changes',
837
+ },
838
+ {
839
+ tool: TOOL_NAMES.GITHUB_SEARCH_CODE,
840
+ reason: 'to find current implementation',
841
+ },
842
+ ],
843
+ },
844
+ [TOOL_NAMES.API_STATUS_CHECK]: {
845
+ fallbackTools: [],
846
+ nextSteps: [
847
+ {
848
+ tool: TOOL_NAMES.GITHUB_SEARCH_REPOSITORIES,
849
+ reason: 'to search accessible repositories',
850
+ },
851
+ {
852
+ tool: TOOL_NAMES.NPM_PACKAGE_SEARCH,
853
+ reason: 'to search public packages',
854
+ },
855
+ ],
856
+ },
857
+ };
858
+ /**
859
+ * Get tool suggestions based on current context
860
+ */
861
+ function getToolSuggestions(currentTool, context) {
862
+ const relationships = TOOL_RELATIONSHIPS[currentTool];
863
+ if (!relationships) {
864
+ return { fallback: [], nextSteps: [], prerequisites: [] };
865
+ }
866
+ // Filter fallback tools based on context
867
+ const fallback = relationships.fallbackTools.filter(item => {
868
+ if (!item.condition)
869
+ return true;
870
+ switch (item.condition) {
871
+ case 'no_results':
872
+ return context.errorType === 'no_results';
873
+ case 'access_denied':
874
+ return context.errorType === 'access_denied';
875
+ default:
876
+ return true;
877
+ }
878
+ });
879
+ // Return next steps only if operation was successful
880
+ const nextSteps = context.hasResults ? relationships.nextSteps : [];
881
+ return {
882
+ fallback,
883
+ nextSteps,
884
+ prerequisites: relationships.prerequisites || [],
885
+ };
886
+ }
887
+
888
+ /**
889
+ * Shared validation utilities for MCP tools
890
+ * Centralized validation logic to reduce duplication across tools
891
+ */
892
+ /**
893
+ * Extracts owner/repo from various query formats
894
+ * Handles: owner/repo, https://github.com/owner/repo, github.com/owner/repo
895
+ */
896
+ /**
897
+ * Creates a standardized tool suggestion message
898
+ */
899
+ function createToolSuggestion(currentTool, suggestedTools) {
900
+ if (suggestedTools.length === 0) {
901
+ return '';
902
+ }
903
+ const suggestions = suggestedTools
904
+ .map(({ tool, reason }) => `• Use ${tool} ${reason}`)
905
+ .join('\n');
906
+ return `\n\nCurrent tool: ${currentTool}\nAlternative tools:\n${suggestions}`;
907
+ }
546
908
 
547
909
  const API_STATUS_CHECK_TOOL_NAME = 'apiStatusCheck';
548
- const DESCRIPTION$9 = `Check GitHub and NPM authentication status. Returns connected status and GitHub organizations for accessing private repositories. No parameters required.`;
910
+ const DESCRIPTION$9 = `Verify API connections and discover available organizations. FIRST STEP for private repository research.
911
+
912
+ AUTHENTICATION VERIFICATION:
913
+ - Check GitHub and NPM CLI authentication status
914
+ - Troubleshoot access issues before extensive searches
915
+ - Verify API connectivity and permissions
916
+
917
+ ORGANIZATION DISCOVERY:
918
+ - List available GitHub organizations for scoped searches
919
+ - Identify accessible private repositories
920
+ - Guide repository search strategy based on permissions
921
+
922
+ WORKFLOW OPTIMIZATION:
923
+ - Run first when dealing with private/organizational repositories
924
+ - Prevents access errors in subsequent tool usage
925
+ - Informs search scope and strategy decisions
926
+ - Essential for comprehensive organizational research`;
549
927
  // Helper function to parse execution results with proper typing
550
928
  function parseExecResult(result) {
551
929
  if (!result.isError && result.content?.[0]?.text) {
@@ -651,6 +1029,19 @@ function registerApiStatusCheckTool(server) {
651
1029
  }
652
1030
  npmConnected = false;
653
1031
  }
1032
+ const { nextSteps } = getToolSuggestions(TOOL_NAMES.API_STATUS_CHECK, {
1033
+ hasResults: true,
1034
+ });
1035
+ const hints = [
1036
+ 'Use user organizations to search private repositories when requested - verify access by checking query and repository structure',
1037
+ ];
1038
+ // Add tool suggestions as hints
1039
+ if (nextSteps.length > 0) {
1040
+ hints.push('Next steps:');
1041
+ nextSteps.forEach(({ tool, reason }) => {
1042
+ hints.push(`- ${tool}: ${reason}`);
1043
+ });
1044
+ }
654
1045
  return createResult({
655
1046
  data: {
656
1047
  login: {
@@ -662,55 +1053,77 @@ function registerApiStatusCheckTool(server) {
662
1053
  connected: npmConnected,
663
1054
  registry: registry || 'https://registry.npmjs.org/',
664
1055
  },
665
- hints: [
666
- 'Use user organizations to search private repositories when requested - verify access by checking query and repository structure',
667
- ],
1056
+ hints,
668
1057
  },
669
1058
  },
670
1059
  });
671
1060
  }
672
1061
  catch (error) {
1062
+ const { nextSteps } = getToolSuggestions(TOOL_NAMES.API_STATUS_CHECK, {
1063
+ });
1064
+ const toolSuggestions = createToolSuggestion(TOOL_NAMES.API_STATUS_CHECK, nextSteps);
673
1065
  return createResult({
674
- error: `${ERROR_MESSAGES.API_STATUS_CHECK_FAILED}\nError: ${error instanceof Error ? error.message : 'Unknown error'}\n\nThis usually indicates a system configuration issue. Please verify GitHub CLI and NPM are properly installed.`,
1066
+ error: getErrorWithSuggestion({
1067
+ baseError: [
1068
+ ERROR_MESSAGES.API_STATUS_CHECK_FAILED,
1069
+ `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
1070
+ '',
1071
+ 'This usually indicates a system configuration issue. Please verify GitHub CLI and NPM are properly installed.',
1072
+ ],
1073
+ suggestion: toolSuggestions,
1074
+ }),
675
1075
  });
676
1076
  }
677
1077
  });
678
1078
  }
679
1079
 
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, {
1080
+ const GITHUB_GET_FILE_CONTENT_TOOL_NAME = 'githubGetFileContent';
1081
+ const DESCRIPTION$8 = `Access GitHub file content for implementation analysis. USE AFTER repository structure validation.
1082
+
1083
+ IMPLEMENTATION ANALYSIS:
1084
+ - Examine configuration files, documentation, key source code
1085
+ - Understand actual implementations and patterns
1086
+ - Extract technical details and architecture decisions
1087
+
1088
+ VALIDATION REQUIREMENTS:
1089
+ - ALWAYS verify repository structure first with githubViewRepoStructure
1090
+ - Confirm file paths exist before accessing
1091
+ - Navigate from known structure to specific files
1092
+
1093
+ CONTENT CAPABILITIES:
1094
+ - Handles text files up to 300KB efficiently
1095
+ - Automatic branch fallback (main/master)
1096
+ - Provides decoded content with metadata
1097
+ - Optimized for code analysis workflows`;
1098
+ function registerFetchGitHubFileContentTool(server) {
1099
+ server.registerTool(GITHUB_GET_FILE_CONTENT_TOOL_NAME, {
684
1100
  description: DESCRIPTION$8,
685
1101
  inputSchema: {
686
1102
  owner: z
687
1103
  .string()
688
1104
  .min(1)
689
1105
  .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')`),
1106
+ .regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/)
1107
+ .describe(`Repository owner/organization name (e.g., 'facebook', 'microsoft'). Do NOT include repository name here.`),
692
1108
  repo: z
693
1109
  .string()
694
1110
  .min(1)
695
1111
  .max(100)
696
- .regex(/^[a-zA-Z0-9._-]+$/, 'Invalid repository name format')
697
- .describe('Repository name (case-sensitive)'),
1112
+ .regex(/^[a-zA-Z0-9._-]+$/)
1113
+ .describe(`Repository name only (e.g., 'react', 'vscode'). Do NOT include owner/org prefix.`),
698
1114
  branch: z
699
1115
  .string()
700
1116
  .min(1)
701
1117
  .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
1118
+ .regex(/^[^\s]+$/)
1119
+ .describe(`Branch name (e.g., 'main', 'master'). Tool will automatically try 'main' and 'master' if specified branch is not found.`),
1120
+ filePath: z
705
1121
  .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.'),
1122
+ .min(1)
1123
+ .describe(`File path from repository root (e.g., 'src/index.js', 'README.md', 'docs/api.md'). Do NOT start with slash.`),
711
1124
  },
712
1125
  annotations: {
713
- title: 'GitHub Repository Explorer',
1126
+ title: 'GitHub File Content - Direct Access',
714
1127
  readOnlyHint: true,
715
1128
  destructiveHint: false,
716
1129
  idempotentHint: true,
@@ -718,63 +1131,28 @@ function registerViewRepositoryStructureTool(server) {
718
1131
  },
719
1132
  }, async (args) => {
720
1133
  try {
721
- const result = await viewRepositoryStructure(args);
1134
+ const result = await fetchGitHubFileContent(args);
722
1135
  return result;
723
1136
  }
724
1137
  catch (error) {
725
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
726
1138
  return createResult({
727
- error: `Failed to explore repository. ${errorMessage}. Verify repository exists and is accessible`,
1139
+ error: 'Failed to fetch file. Verify path with github_get_contents first',
728
1140
  });
729
1141
  }
730
1142
  });
731
1143
  }
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);
1144
+ async function fetchGitHubFileContent(params) {
1145
+ const cacheKey = generateCacheKey('gh-file-content', params);
738
1146
  return withCache(cacheKey, async () => {
739
- const { owner, repo, branch, path = '' } = params;
1147
+ const { owner, repo, branch, filePath } = params;
740
1148
  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) {
1149
+ // Try to fetch file content directly
1150
+ const apiPath = `/repos/${owner}/${repo}/contents/${filePath}`;
1151
+ const result = await executeGitHubCommand('api', [apiPath], {
1152
+ cache: false,
1153
+ });
1154
+ if (result.isError) {
1155
+ const errorMsg = result.content[0].text;
778
1156
  // Check repository existence only after content fetch fails
779
1157
  const repoCheckResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
780
1158
  cache: false,
@@ -783,7 +1161,7 @@ async function viewRepositoryStructure(params) {
783
1161
  const repoErrorMsg = repoCheckResult.content[0].text;
784
1162
  if (repoErrorMsg.includes('404')) {
785
1163
  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.`,
1164
+ 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`,
787
1165
  });
788
1166
  }
789
1167
  else if (repoErrorMsg.includes('403')) {
@@ -792,277 +1170,102 @@ async function viewRepositoryStructure(params) {
792
1170
  });
793
1171
  }
794
1172
  }
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.`,
1173
+ // Try fallback branches if original branch fails
1174
+ if (errorMsg.includes('404') &&
1175
+ branch !== 'main' &&
1176
+ branch !== 'master') {
1177
+ const fallbackBranches = ['main', 'master'];
1178
+ const triedBranches = [branch];
1179
+ for (const fallbackBranch of fallbackBranches) {
1180
+ if (triedBranches.includes(fallbackBranch))
1181
+ continue;
1182
+ triedBranches.push(fallbackBranch);
1183
+ const fallbackPath = `/repos/${owner}/${repo}/contents/${filePath}?ref=${fallbackBranch}`;
1184
+ const fallbackResult = await executeGitHubCommand('api', [fallbackPath], {
1185
+ cache: false,
806
1186
  });
1187
+ if (!fallbackResult.isError) {
1188
+ return await processFileContent(fallbackResult, owner, repo, fallbackBranch, filePath);
1189
+ }
807
1190
  }
1191
+ return createResult({
1192
+ error: `File not found in any common branches (tried: ${triedBranches.join(', ')}). File might have been moved or deleted. Use github_search_code to find current location.`,
1193
+ });
808
1194
  }
809
- else if (errorMsg.includes('403') || errorMsg.includes('Forbidden')) {
1195
+ // Handle common errors with more context
1196
+ if (errorMsg.includes('404')) {
1197
+ const searchSuggestion = await suggestCodeSearchFallback(owner, filePath);
810
1198
  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.`,
1199
+ error: `File "${filePath}" not found in branch "${branch}".
1200
+
1201
+ Quick fixes:
1202
+ • Use github_view_repo_structure to verify path exists
1203
+ • Check for typos in file path
1204
+ • Try different branch (main/master/develop)${searchSuggestion}
1205
+
1206
+ Alternative strategies:
1207
+ • Use github_search_code with query="filename:${filePath.split('/').pop()}" owner="${owner}"
1208
+ • Use github_search_code with query="path:${filePath}" to find similar paths`,
1209
+ });
1210
+ }
1211
+ else if (errorMsg.includes('403')) {
1212
+ return createResult({
1213
+ error: `Access denied to "${filePath}" in "${owner}/${repo}".
1214
+
1215
+ Possible causes & solutions:
1216
+ • Private repository: use api_status_check to verify permissions
1217
+ • File in private directory: check repository access level
1218
+ • Rate limiting: wait 5-10 minutes and retry
1219
+ • Authentication: run gh auth login
1220
+
1221
+ Alternative approaches:
1222
+ • Use github_search_code with query="path:${filePath}" owner="${owner}"
1223
+ • Use github_view_repo_structure to explore accessible paths
1224
+ • Check repository on GitHub.com for public access`,
1225
+ });
1226
+ }
1227
+ else if (errorMsg.includes('maxBuffer') ||
1228
+ errorMsg.includes('stdout maxBuffer length exceeded')) {
1229
+ return createResult({
1230
+ error: `File "${filePath}" is too large (>300KB).
1231
+
1232
+ Alternative strategies:
1233
+ • Use github_search_code to search within the file
1234
+ • Download directly from: https://github.com/${owner}/${repo}/blob/${branch}/${filePath}
1235
+ • Use github_view_repo_structure to find smaller related files
1236
+ • Look for configuration or summary files instead`,
812
1237
  });
813
1238
  }
814
1239
  else {
815
- const searchSuggestion = path
816
- ? await suggestPathSearchFallback(owner, path)
817
- : '';
1240
+ const searchSuggestion = await suggestCodeSearchFallback(owner, filePath);
818
1241
  return createResult({
819
- error: `Failed to access "${owner}/${repo}": ${errorMsg}. Check network connection and repository permissions.${searchSuggestion}`,
1242
+ error: `Failed to fetch "${filePath}": ${errorMsg}
1243
+
1244
+ Troubleshooting steps:
1245
+ 1. Verify repository exists: github_view_repo_structure
1246
+ 2. Check network connection and GitHub status
1247
+ 3. Verify authentication: gh auth status
1248
+ 4. Try different branch names${searchSuggestion}
1249
+
1250
+ Recovery strategies:
1251
+ • Use github_search_code for content discovery
1252
+ • Try github_search_repos to find similar repositories
1253
+ • Check file on GitHub.com: https://github.com/${owner}/${repo}/blob/${branch}/${filePath}`,
820
1254
  });
821
1255
  }
822
1256
  }
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
- const GITHUB_GET_FILE_CONTENT_TOOL_NAME = 'githubGetFileContent';
931
- const DESCRIPTION$7 = `Fetch file content from GitHub repositories. Use ${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME} first to explore repository structure and find exact file paths. Supports automatic branch fallback (main/master) and handles files up to 300KB. Parameters: owner (required - GitHub username/org), repo (required - repository name), branch (required), filePath (required).`;
932
- function registerFetchGitHubFileContentTool(server) {
933
- server.registerTool(GITHUB_GET_FILE_CONTENT_TOOL_NAME, {
934
- description: DESCRIPTION$7,
935
- inputSchema: {
936
- owner: z
937
- .string()
938
- .min(1)
939
- .max(100)
940
- .regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/)
941
- .describe(`Repository owner/org name (e.g., 'microsoft', 'google', NOT 'microsoft/vscode')`),
942
- repo: z
943
- .string()
944
- .min(1)
945
- .max(100)
946
- .regex(/^[a-zA-Z0-9._-]+$/)
947
- .describe(`Repository name only (e.g., 'vscode', 'react', NOT 'microsoft/vscode')`),
948
- branch: z
949
- .string()
950
- .min(1)
951
- .max(255)
952
- .regex(/^[^\s]+$/)
953
- .describe(`Branch name. Falls back to main/master if not found`),
954
- filePath: z
955
- .string()
956
- .min(1)
957
- .describe(`Exact file path from repo root (e.g., src/index.js, README.md)`),
958
- },
959
- annotations: {
960
- title: 'GitHub File Content - Direct Access',
961
- readOnlyHint: true,
962
- destructiveHint: false,
963
- idempotentHint: true,
964
- openWorldHint: true,
965
- },
966
- }, async (args) => {
967
- try {
968
- const result = await fetchGitHubFileContent(args);
969
- return result;
970
- }
971
- catch (error) {
972
- return createResult({
973
- error: 'Failed to fetch file. Verify path with github_get_contents first',
974
- });
975
- }
976
- });
977
- }
978
- async function fetchGitHubFileContent(params) {
979
- const cacheKey = generateCacheKey('gh-file-content', params);
980
- return withCache(cacheKey, async () => {
981
- const { owner, repo, branch, filePath } = params;
982
- try {
983
- // Try to fetch file content directly
984
- const apiPath = `/repos/${owner}/${repo}/contents/${filePath}`;
985
- const result = await executeGitHubCommand('api', [apiPath], {
986
- cache: false,
987
- });
988
- if (result.isError) {
989
- const errorMsg = result.content[0].text;
990
- // Check repository existence only after content fetch fails
991
- const repoCheckResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
992
- cache: false,
993
- });
994
- if (repoCheckResult.isError) {
995
- const repoErrorMsg = repoCheckResult.content[0].text;
996
- if (repoErrorMsg.includes('404')) {
997
- return createResult({
998
- error: `Repository "${owner}/${repo}" not found. It might have been deleted, renamed, or made private. Use github_search_code to find current location.`,
999
- });
1000
- }
1001
- else if (repoErrorMsg.includes('403')) {
1002
- return createResult({
1003
- error: `Repository "${owner}/${repo}" exists but access is denied. Repository might be private or archived. Use api_status_check to verify permissions.`,
1004
- });
1005
- }
1006
- }
1007
- // Try fallback branches if original branch fails
1008
- if (errorMsg.includes('404') &&
1009
- branch !== 'main' &&
1010
- branch !== 'master') {
1011
- const fallbackBranches = ['main', 'master'];
1012
- const triedBranches = [branch];
1013
- for (const fallbackBranch of fallbackBranches) {
1014
- if (triedBranches.includes(fallbackBranch))
1015
- continue;
1016
- triedBranches.push(fallbackBranch);
1017
- const fallbackPath = `/repos/${owner}/${repo}/contents/${filePath}?ref=${fallbackBranch}`;
1018
- const fallbackResult = await executeGitHubCommand('api', [fallbackPath], {
1019
- cache: false,
1020
- });
1021
- if (!fallbackResult.isError) {
1022
- return await processFileContent(fallbackResult, owner, repo, fallbackBranch, filePath);
1023
- }
1024
- }
1025
- return createResult({
1026
- error: `File not found in any common branches (tried: ${triedBranches.join(', ')}). File might have been moved or deleted. Use github_search_code to find current location.`,
1027
- });
1028
- }
1029
- // Handle common errors with more context
1030
- if (errorMsg.includes('404')) {
1031
- const searchSuggestion = await suggestCodeSearchFallback(owner, filePath);
1032
- return createResult({
1033
- error: `File "${filePath}" not found in branch "${branch}". Use github_view_repo_structure to verify path.${searchSuggestion}`,
1034
- });
1035
- }
1036
- else if (errorMsg.includes('403')) {
1037
- return createResult({
1038
- error: `Access denied to "${filePath}" in "${owner}/${repo}". Repository or file might be private/archived. Use api_status_check to verify permissions, or github_search_code with query="path:${filePath}" owner="${owner}" to find in accessible repositories.`,
1039
- });
1040
- }
1041
- else if (errorMsg.includes('maxBuffer') ||
1042
- errorMsg.includes('stdout maxBuffer length exceeded')) {
1043
- return createResult({
1044
- error: `File "${filePath}" is too large (>300KB). Use github_search_code with query="path:${filePath}" to search within the file or download directly from GitHub.`,
1045
- });
1046
- }
1047
- else {
1048
- const searchSuggestion = await suggestCodeSearchFallback(owner, filePath);
1049
- return createResult({
1050
- error: `Failed to fetch "${filePath}". Error: ${errorMsg}. Verify repository name, branch, and file path.${searchSuggestion}`,
1051
- });
1052
- }
1053
- }
1054
- return await processFileContent(result, owner, repo, branch, filePath);
1055
- }
1056
- catch (error) {
1057
- const errorMessage = error.message;
1058
- if (errorMessage.includes('maxBuffer') ||
1059
- errorMessage.includes('stdout maxBuffer length exceeded')) {
1060
- return createResult({
1061
- error: `File "${filePath}" is too large (>300KB). Use github_search_code to search within the file or download directly from GitHub.`,
1062
- });
1063
- }
1064
- return createResult({
1065
- error: `Unexpected error fetching "${filePath}": ${errorMessage}. Check network connection and try again.`,
1257
+ return await processFileContent(result, owner, repo, branch, filePath);
1258
+ }
1259
+ catch (error) {
1260
+ const errorMessage = error.message;
1261
+ if (errorMessage.includes('maxBuffer') ||
1262
+ errorMessage.includes('stdout maxBuffer length exceeded')) {
1263
+ return createResult({
1264
+ error: `File "${filePath}" is too large (>300KB). Use github_search_code to search within the file or download directly from GitHub.`,
1265
+ });
1266
+ }
1267
+ return createResult({
1268
+ error: `Unexpected error fetching "${filePath}": ${errorMessage}. Check network connection and try again.`,
1066
1269
  });
1067
1270
  }
1068
1271
  });
@@ -1146,55 +1349,73 @@ async function suggestCodeSearchFallback(owner, filePath) {
1146
1349
  }
1147
1350
 
1148
1351
  const GITHUB_SEARCH_CODE_TOOL_NAME = 'githubSearchCode';
1149
- const DESCRIPTION$6 = `Search code across GitHub repositories using GitHub's code search API.
1150
-
1151
- Search Syntax (all terms must be present with AND logic):
1152
- - Multiple words: react lifecycle → finds files with BOTH "react" AND "lifecycle" (separate words)
1153
- - Exact phrases: "error handling" → finds exact phrase "error handling" (quoted phrase)
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
1158
-
1159
- Key difference: Quotes create exact phrases, no quotes = individual words (all must be present).
1160
- Start with 1-2 terms, add more to narrow results. Use filters for precision.`;
1352
+ const DESCRIPTION$7 = `Search code across GitHub repositories using GitHub's code search API via GitHub CLI.
1353
+
1354
+ SEARCH STRATEGY FOR BEST RESULTS:
1355
+
1356
+ TERM OPTIMIZATION (IMPORTANT):
1357
+ - BEST: Single terms for maximum coverage and relevance
1358
+ - GOOD: 2 terms when you need both to be present
1359
+ - RESTRICTIVE: 3+ terms - very specific but may miss relevant results
1360
+ - Example: "useState" (best coverage) vs "react hook useState" (specific but restrictive)
1361
+
1362
+ MULTI-SEARCH STRATEGY:
1363
+ - Use SEPARATE searches for different aspects instead of complex single queries
1364
+ - Example: Search "authentication" separately, then "login" separately
1365
+ - Separate searches provide broader coverage than restrictive AND logic
1366
+
1367
+ Search Logic:
1368
+ - Multiple terms = ALL must be present (AND logic) - very restrictive
1369
+ - Single terms = maximum results and coverage
1370
+ - Quoted phrases = exact phrase matching
1371
+ - Mixed queries: "exact phrase" additional_term (phrase + term)
1372
+
1373
+ Quote Usage:
1374
+ - Single terms: NO quotes (useState, authentication)
1375
+ - Multi-word phrases: WITH quotes ("error handling", "user authentication")
1376
+ - Mixed queries: "exact phrase" single_term another_term
1377
+
1378
+ Filter Usage:
1379
+ - All filters use GitHub CLI flags (--language, --owner, --repo, etc.)
1380
+ - Combine filters to narrow scope: language + owner, repo + filename
1381
+ - Never use filters on exploratory searches - use to refine when too many results`;
1161
1382
  function registerGitHubSearchCodeTool(server) {
1162
1383
  server.registerTool(GITHUB_SEARCH_CODE_TOOL_NAME, {
1163
- description: DESCRIPTION$6,
1384
+ description: DESCRIPTION$7,
1164
1385
  inputSchema: {
1165
1386
  query: z
1166
1387
  .string()
1167
1388
  .min(1)
1168
- .describe('Search query with AND logic between terms. Multiple words require ALL to be present. Use quotes for exact phrases. Examples: react lifecycle (both words), "error handling" (exact phrase), async await "try catch" (words + phrase), "function definition" variable scope (phrase + words)'),
1389
+ .describe('Search query with AND logic between terms. OPTIMIZATION: Single terms give best coverage, 2 terms when both needed, 3+ terms very restrictive. Multiple words require ALL to be present. Use quotes for exact phrases. Examples: "useState" (best coverage), "error handling" (exact phrase), "react hook useState" (specific but restrictive).'),
1169
1390
  language: z
1170
1391
  .string()
1171
1392
  .optional()
1172
- .describe('Programming language filter (javascript, python, typescript, go, etc). Narrows search to specific language files. Use for language-specific searches.'),
1393
+ .describe('Programming language filter. Uses --language CLI flag. Narrows search to specific language files. Use for language-specific searches.'),
1173
1394
  owner: z
1174
1395
  .union([z.string(), z.array(z.string())])
1175
1396
  .optional()
1176
- .describe('Repository owner or organization name(s) to search within. Format: "microsoft" or ["facebook", "google"]. Drastically narrows search scope. Do NOT use owner/repo format.'),
1397
+ .describe('Repository owner/organization name(s) to search within (e.g., "facebook", ["google", "microsoft"]). Uses --owner CLI flag for organization-wide search. Can be combined with repo parameter for specific repository search. Do NOT use owner/repo format - just the organization/username.'),
1177
1398
  repo: z
1178
1399
  .union([z.string(), z.array(z.string())])
1179
1400
  .optional()
1180
- .describe('Filter on specific repository(ies). Format: "owner/repo" or ["microsoft/vscode", "facebook/react"]. Use for repository-specific searches.'),
1401
+ .describe('Filter on specific repository(ies). Uses --repo CLI flag. Two usage patterns: (1) Use with owner parameter - provide just repo name (e.g., owner="facebook", repo="react" → --repo=facebook/react), or (2) Use alone - provide full "owner/repo" format (e.g., "facebook/react" → --repo=facebook/react).'),
1181
1402
  filename: z
1182
1403
  .string()
1183
1404
  .optional()
1184
- .describe('Target specific filename or pattern. Examples: "package.json", "*.test.js", "Dockerfile". Use for file-specific searches.'),
1405
+ .describe('Target specific filename or pattern. Uses --filename CLI flag. Use for file-specific searches.'),
1185
1406
  extension: z
1186
1407
  .string()
1187
1408
  .optional()
1188
- .describe('File extension filter (js, py, ts, go, etc). Alternative to language parameter. Examples: "js", "py", "tsx".'),
1409
+ .describe('File extension filter. Uses --extension CLI flag. Alternative to language parameter.'),
1189
1410
  match: z
1190
- .union([z.enum(['file', 'path']), z.array(z.enum(['file', 'path']))])
1411
+ .enum(['file', 'path'])
1191
1412
  .optional()
1192
- .describe('Search scope: "file" for file content (default), "path" for filenames/paths, or ["file", "path"] for both. Controls where to search for terms.'),
1413
+ .describe('Search scope: "file" for file content (default), "path" for filenames/paths. Uses --match CLI flag. Single value only - multiple scopes not supported by GitHub CLI.'),
1193
1414
  size: z
1194
1415
  .string()
1195
1416
  .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid size format. Use: ">10", ">=5", "<100", "<=50", "10..100", or exact number "50"')
1196
1417
  .optional()
1197
- .describe('File size filter in KB. Format: ">10" (larger than), ">=5" (at least), "<100" (smaller than), "<=50" (at most), "10..50" (range), "25" (exact). Examples from docs: ">10", "10..50", "<100"'),
1418
+ .describe('File size filter in KB. Uses --size CLI flag. Format: ">N" (larger than), "<N" (smaller than), "N..M" (range), "N" (exact).'),
1198
1419
  limit: z
1199
1420
  .number()
1200
1421
  .int()
@@ -1213,11 +1434,6 @@ function registerGitHubSearchCodeTool(server) {
1213
1434
  },
1214
1435
  }, async (args) => {
1215
1436
  try {
1216
- // Validate parameter combinations
1217
- const validationError = validateSearchParameters(args);
1218
- if (validationError) {
1219
- return createResult({ error: validationError });
1220
- }
1221
1437
  const result = await searchGitHubCode(args);
1222
1438
  if (result.isError) {
1223
1439
  return result;
@@ -1252,45 +1468,79 @@ function registerGitHubSearchCodeTool(server) {
1252
1468
  });
1253
1469
  }
1254
1470
  /**
1255
- * Handles various search errors and returns a formatted CallToolResult.
1471
+ * Handles various search errors and returns a formatted CallToolResult with smart fallbacks.
1256
1472
  */
1257
1473
  function handleSearchError(errorMessage) {
1258
- // Common GitHub search errors with helpful suggestions
1259
- if (errorMessage.includes('JSON')) {
1474
+ // Rate limit with smart timing guidance
1475
+ if (errorMessage.includes('rate limit') || errorMessage.includes('403')) {
1260
1476
  return createResult({
1261
- error: ERROR_MESSAGES.CLI_INVALID_RESPONSE,
1477
+ error: `GitHub API rate limit reached. Try again in 5-10 minutes, or use these strategies:
1478
+ • Search fewer terms per query
1479
+ • Use owner/repo filters to narrow scope
1480
+ • Try npm package search for package-related queries
1481
+ • Use separate searches instead of complex queries`,
1262
1482
  });
1263
1483
  }
1264
- if (errorMessage.includes('authentication')) {
1484
+ // Authentication with clear next steps
1485
+ if (errorMessage.includes('authentication') || errorMessage.includes('401')) {
1265
1486
  return createResult({
1266
- error: createAuthenticationError(),
1487
+ error: `GitHub authentication required. Fix with:
1488
+ 1. Run: gh auth login
1489
+ 2. Verify access: gh auth status
1490
+ 3. For private repos: use api_status_check to verify org access`,
1267
1491
  });
1268
1492
  }
1269
- if (errorMessage.includes('rate limit')) {
1493
+ // Network/timeout with fallback suggestions
1494
+ if (errorMessage.includes('timed out') || errorMessage.includes('network')) {
1270
1495
  return createResult({
1271
- error: createRateLimitError(true),
1272
- });
1273
- }
1274
- if (errorMessage.includes('timed out')) {
1275
- return createResult({
1276
- error: ERROR_MESSAGES.SEARCH_TIMEOUT,
1496
+ error: `Network timeout. Try these alternatives:
1497
+ • Reduce search scope with owner or language filters
1498
+ • Use github_search_repos to find repositories first
1499
+ Try npm package search for package discovery
1500
+ Check network connection and retry`,
1277
1501
  });
1278
1502
  }
1503
+ // Invalid query with specific fixes
1279
1504
  if (errorMessage.includes('validation failed') ||
1280
1505
  errorMessage.includes('Invalid query')) {
1281
1506
  return createResult({
1282
- error: ERROR_MESSAGES.INVALID_QUERY_SYNTAX,
1507
+ error: `Invalid search query. Common fixes:
1508
+ • Remove special characters: ()[]{}*?^$|.\\
1509
+ • Use quotes only for exact phrases: "error handling"
1510
+ • Avoid escaped quotes: use term instead of "term"
1511
+ • Try broader terms: "react" instead of "React.Component"`,
1283
1512
  });
1284
1513
  }
1514
+ // Repository not found with discovery suggestions
1285
1515
  if (errorMessage.includes('repository not found') ||
1286
1516
  errorMessage.includes('owner not found')) {
1287
1517
  return createResult({
1288
- error: ERROR_MESSAGES.REPO_OR_OWNER_NOT_FOUND,
1518
+ error: `Repository/owner not found. Discovery strategies:
1519
+ • Use github_search_repos to find correct names
1520
+ • Check for typos in owner/repo names
1521
+ • Try without owner filter for broader search
1522
+ • Use npm package search if looking for packages`,
1523
+ });
1524
+ }
1525
+ // JSON parsing with system guidance
1526
+ if (errorMessage.includes('JSON')) {
1527
+ return createResult({
1528
+ error: `GitHub CLI response parsing failed. System issue - try:
1529
+ • Update GitHub CLI: gh extension upgrade
1530
+ • Retry in a few moments
1531
+ • Use github_search_repos as alternative
1532
+ • Check gh auth status for authentication`,
1289
1533
  });
1290
1534
  }
1291
- // Generic fallback with guidance
1535
+ // Generic fallback with progressive strategy
1292
1536
  return createResult({
1293
- error: `Code search failed: ${errorMessage}\n${SUGGESTIONS.SIMPLIFY_QUERY}`,
1537
+ error: `Code search failed: ${errorMessage}
1538
+
1539
+ Progressive recovery strategy:
1540
+ 1. Try broader search terms
1541
+ 2. Use github_search_repos to find repositories
1542
+ 3. Use npm package search for package-related queries
1543
+ 4. Check github CLI status: gh auth status`,
1294
1544
  });
1295
1545
  }
1296
1546
  /**
@@ -1308,6 +1558,10 @@ function transformToOptimizedFormat$1(items) {
1308
1558
  : [0, 0]) || [],
1309
1559
  })) || [],
1310
1560
  url: singleRepo ? item.path : simplifyGitHubUrl(item.url),
1561
+ repository: {
1562
+ nameWithOwner: item.repository.nameWithOwner,
1563
+ url: item.repository.url,
1564
+ },
1311
1565
  }));
1312
1566
  const result = {
1313
1567
  items: optimizedItems,
@@ -1333,8 +1587,8 @@ function extractSingleRepository$1(items) {
1333
1587
  return allSameRepo ? firstRepo : null;
1334
1588
  }
1335
1589
  /**
1336
- * Build command line arguments for GitHub CLI with improved parameter handling.
1337
- * Preserves quoted phrases and supports both AND search and exact phrase matching.
1590
+ * Build command line arguments for GitHub CLI following the exact CLI format.
1591
+ * Uses proper flags (--flag=value) instead of qualifiers where appropriate.
1338
1592
  */
1339
1593
  function buildGitHubCliArgs(params) {
1340
1594
  const args = ['code'];
@@ -1344,39 +1598,52 @@ function buildGitHubCliArgs(params) {
1344
1598
  if (searchQuery) {
1345
1599
  args.push(searchQuery);
1346
1600
  }
1347
- // Add extracted qualifiers from the query
1601
+ // Add extracted qualifiers from the query (these should remain as qualifiers)
1348
1602
  extractedQualifiers.forEach(qualifier => {
1349
1603
  args.push(qualifier);
1350
1604
  });
1351
- // Add explicit parameters as qualifiers
1605
+ // Add explicit parameters as CLI flags (following GitHub CLI format)
1352
1606
  if (params.language && !params.query.includes('language:')) {
1353
- args.push(`language:${params.language}`);
1607
+ args.push(`--language=${params.language}`);
1608
+ }
1609
+ // Handle owner and repo parameters properly
1610
+ if (params.repo && !params.query.includes('repo:')) {
1611
+ const repos = Array.isArray(params.repo) ? params.repo : [params.repo];
1612
+ repos.forEach(repo => {
1613
+ // If both owner and repo are provided, combine them for --repo flag
1614
+ if (params.owner && !repo.includes('/')) {
1615
+ const owners = Array.isArray(params.owner)
1616
+ ? params.owner
1617
+ : [params.owner];
1618
+ owners.forEach(owner => args.push(`--repo=${owner}/${repo}`));
1619
+ }
1620
+ else {
1621
+ // Repo is already in owner/repo format or no owner provided
1622
+ args.push(`--repo=${repo}`);
1623
+ }
1624
+ });
1354
1625
  }
1355
- if (params.owner &&
1626
+ else if (params.owner &&
1356
1627
  !params.query.includes('org:') &&
1357
1628
  !params.query.includes('user:')) {
1629
+ // Only owner provided, no repo - use --owner flag for organization-wide search
1358
1630
  const owners = Array.isArray(params.owner) ? params.owner : [params.owner];
1359
- owners.forEach(owner => args.push(`org:${owner}`));
1360
- }
1361
- if (params.repo && !params.query.includes('repo:')) {
1362
- const repos = Array.isArray(params.repo) ? params.repo : [params.repo];
1363
- repos.forEach(repo => args.push(`repo:${repo}`));
1631
+ owners.forEach(owner => args.push(`--owner=${owner}`));
1364
1632
  }
1365
1633
  if (params.filename && !params.query.includes('filename:')) {
1366
- args.push(`filename:${params.filename}`);
1634
+ args.push(`--filename=${params.filename}`);
1367
1635
  }
1368
1636
  if (params.extension && !params.query.includes('extension:')) {
1369
- args.push(`extension:${params.extension}`);
1637
+ args.push(`--extension=${params.extension}`);
1370
1638
  }
1371
1639
  if (params.size && !params.query.includes('size:')) {
1372
- args.push(`size:${params.size}`);
1640
+ args.push(`--size=${params.size}`);
1373
1641
  }
1374
- // Handle match parameter
1642
+ // Handle match parameter - use --match flag
1375
1643
  if (params.match) {
1376
- const matches = Array.isArray(params.match) ? params.match : [params.match];
1377
- args.push(`in:${matches.join(',')}`);
1644
+ args.push(`--match=${params.match}`);
1378
1645
  }
1379
- // Add limit
1646
+ // Add limit flag
1380
1647
  if (params.limit) {
1381
1648
  args.push(`--limit=${params.limit}`);
1382
1649
  }
@@ -1396,69 +1663,38 @@ async function searchGitHubCode(params) {
1396
1663
  }
1397
1664
  catch (error) {
1398
1665
  const errorMessage = error.message || '';
1399
- return handleSearchError(errorMessage); // Delegating error handling
1666
+ return handleSearchError(errorMessage);
1400
1667
  }
1401
1668
  });
1402
1669
  }
1403
1670
  /**
1404
- * Enhanced validation with helpful suggestions
1671
+ * Parse search query to preserve quoted phrases and extract qualifiers.
1672
+ * Handles:
1673
+ * - Quoted phrases: "error handling" -> kept as single unit
1674
+ * - Multiple terms: react lifecycle -> both terms for AND search
1675
+ * - Mixed: "error handling" debug -> phrase + term
1676
+ * - Qualifiers: language:javascript -> extracted separately
1677
+ * - Quote escaping issues: automatically fixes common mistakes
1405
1678
  */
1406
- function validateSearchParameters(params) {
1407
- // Query validation
1408
- if (!params.query.trim()) {
1409
- return ERROR_MESSAGES.EMPTY_QUERY;
1410
- }
1411
- if (params.query.length > 1000) {
1412
- return ERROR_MESSAGES.QUERY_TOO_LONG_1000;
1679
+ function parseSearchQuery(query) {
1680
+ const qualifiers = [];
1681
+ const searchTerms = [];
1682
+ // Clean up common quote escaping issues
1683
+ let cleanedQuery = query;
1684
+ // Fix escaped quotes that shouldn't be escaped
1685
+ if (cleanedQuery.includes('\\"') && !cleanedQuery.includes(' ')) {
1686
+ cleanedQuery = cleanedQuery.replace(/\\"/g, '');
1413
1687
  }
1414
- // Repository validation - ensure owner is provided correctly
1415
- if (params.owner &&
1416
- typeof params.owner === 'string' &&
1417
- params.owner.includes('/')) {
1418
- return 'Owner parameter should contain only the username/org name, not owner/repo format. For repository-specific searches, use org: or user: qualifiers in the query.';
1688
+ // Fix single-word queries wrapped in quotes unnecessarily
1689
+ if (cleanedQuery.startsWith('"') &&
1690
+ cleanedQuery.endsWith('"') &&
1691
+ !cleanedQuery.slice(1, -1).includes(' ')) {
1692
+ cleanedQuery = cleanedQuery.slice(1, -1);
1419
1693
  }
1420
- if (Array.isArray(params.owner)) {
1421
- const hasSlashFormat = params.owner.some(owner => owner.includes('/'));
1422
- if (hasSlashFormat) {
1423
- return 'Owner parameter should contain only usernames/org names, not owner/repo format. For repository-specific searches, use repo: qualifier in the query.';
1424
- }
1425
- }
1426
- // Add validation for file size limit
1427
- if (params.size) {
1428
- if (!/^([<>]\d+|\d+\.\.\d+)$/.test(params.size)) {
1429
- return ERROR_MESSAGES.INVALID_SIZE_FORMAT;
1430
- }
1431
- }
1432
- // Validate search scope
1433
- if (params.match) {
1434
- const validScopes = ['file', 'path'];
1435
- const scopes = Array.isArray(params.match) ? params.match : [params.match];
1436
- if (!scopes.every(scope => validScopes.includes(scope))) {
1437
- return ERROR_MESSAGES.INVALID_SEARCH_SCOPE;
1438
- }
1439
- }
1440
- // Note about repository limitations (This is a note, not a hard error)
1441
- // This return statement was returning null before, so it shouldn't be an issue
1442
- // if (params.repo || params.owner) {
1443
- // return null; // Return warning about repository limitations
1444
- // }
1445
- return null; // No validation errors
1446
- }
1447
- /**
1448
- * Parse search query to preserve quoted phrases and extract qualifiers.
1449
- * Handles:
1450
- * - Quoted phrases: "error handling" -> kept as single unit
1451
- * - Multiple terms: react lifecycle -> both terms for AND search
1452
- * - Mixed: "error handling" debug -> phrase + term
1453
- * - Qualifiers: language:javascript -> extracted separately
1454
- */
1455
- function parseSearchQuery(query) {
1456
- const qualifiers = [];
1457
- const searchTerms = [];
1458
1694
  // Regular expression to match quoted strings or individual words/qualifiers
1459
1695
  const tokenRegex = /"([^"]+)"|([^\s]+)/g;
1460
1696
  let match;
1461
- while ((match = tokenRegex.exec(query)) !== null) {
1697
+ while ((match = tokenRegex.exec(cleanedQuery)) !== null) {
1462
1698
  const token = match[1] || match[2]; // match[1] is quoted content, match[2] is unquoted
1463
1699
  // Check if it's a qualifier (contains : but not inside quotes)
1464
1700
  if (!match[1] && token.includes(':') && /^[a-zA-Z]+:/.test(token)) {
@@ -1482,10 +1718,10 @@ function parseSearchQuery(query) {
1482
1718
  }
1483
1719
 
1484
1720
  const GITHUB_SEARCH_COMMITS_TOOL_NAME = 'githubSearchCommits';
1485
- const DESCRIPTION$5 = `Search commit history across GitHub repositories. Supports filtering by repository, author, date ranges, and commit attributes. Parameters: query (optional), owner (optional - GitHub username/org, NOT owner/repo), repo (optional - repository name, use with owner for specific repo), author (optional), authorName (optional), authorEmail (optional), committer (optional), committerName (optional), committerEmail (optional), authorDate (optional), committerDate (optional), hash (optional), parent (optional), tree (optional), merge (optional), visibility (optional), limit (optional), sort (optional), order (optional).`;
1721
+ 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
1722
  function registerGitHubSearchCommitsTool(server) {
1487
1723
  server.registerTool(GITHUB_SEARCH_COMMITS_TOOL_NAME, {
1488
- description: DESCRIPTION$5,
1724
+ description: DESCRIPTION$6,
1489
1725
  inputSchema: {
1490
1726
  query: z
1491
1727
  .string()
@@ -1495,11 +1731,11 @@ function registerGitHubSearchCommitsTool(server) {
1495
1731
  owner: z
1496
1732
  .string()
1497
1733
  .optional()
1498
- .describe('Repository owner/org name only (e.g., "microsoft", "google", NOT "microsoft/vscode"). Use with repo parameter for repository-specific searches.'),
1734
+ .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
1735
  repo: z
1500
1736
  .string()
1501
1737
  .optional()
1502
- .describe('Repository name only (e.g., "vscode", "react", NOT "owner/repo"). Must be used together with owner parameter.'),
1738
+ .describe('Repository name only (e.g., "react", "vscode"). Do NOT include owner prefix. Must be used together with owner parameter.'),
1503
1739
  // Author filters
1504
1740
  author: z
1505
1741
  .string()
@@ -1749,10 +1985,10 @@ function buildGitHubCommitCliArgs(params) {
1749
1985
  }
1750
1986
 
1751
1987
  const GITHUB_SEARCH_ISSUES_TOOL_NAME = 'githubSearchIssues';
1752
- const DESCRIPTION$4 = `Search GitHub issues for bug discovery and feature analysis. Supports filtering by state, labels, assignee, dates, and more. Parameters: query (required), owner (optional - GitHub username/org, NOT owner/repo), repo (optional - repository name, use with owner for specific repo), app (optional), archived (optional), assignee (optional), author (optional), closed (optional), commenter (optional), comments (optional), created (optional), includePrs (optional), interactions (optional), involves (optional), labels (optional), language (optional), locked (optional), match (optional), mentions (optional), milestone (optional), noAssignee (optional), noLabel (optional), noMilestone (optional), noProject (optional), project (optional), reactions (optional), state (optional), teamMentions (optional), updated (optional), visibility (optional), sort (optional), order (optional), limit (optional).`;
1988
+ 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
1989
  function registerSearchGitHubIssuesTool(server) {
1754
1990
  server.registerTool(GITHUB_SEARCH_ISSUES_TOOL_NAME, {
1755
- description: DESCRIPTION$4,
1991
+ description: DESCRIPTION$5,
1756
1992
  inputSchema: {
1757
1993
  query: z
1758
1994
  .string()
@@ -1762,11 +1998,11 @@ function registerSearchGitHubIssuesTool(server) {
1762
1998
  .string()
1763
1999
  .min(1)
1764
2000
  .optional()
1765
- .describe('Repository owner/org name only (e.g., "microsoft", "google", NOT "microsoft/vscode"). Use with repo parameter for repository-specific searches.'),
2001
+ .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
2002
  repo: z
1767
2003
  .string()
1768
2004
  .optional()
1769
- .describe('Repository name only (e.g., "vscode", "react", NOT "owner/repo"). Must be used together with owner parameter.'),
2005
+ .describe('Repository name only (e.g., "react", "vscode"). Do NOT include owner prefix. Must be used together with owner parameter.'),
1770
2006
  app: z
1771
2007
  .string()
1772
2008
  .optional()
@@ -1820,7 +2056,7 @@ function registerSearchGitHubIssuesTool(server) {
1820
2056
  label: z
1821
2057
  .union([z.string(), z.array(z.string())])
1822
2058
  .optional()
1823
- .describe('Label names (bug, feature, etc.). Can be single string or array.'),
2059
+ .describe('Label names. Can be single string or array.'),
1824
2060
  language: z.string().optional().describe('Repository language'),
1825
2061
  locked: z.boolean().optional().describe('Conversation locked status'),
1826
2062
  match: z
@@ -1961,6 +2197,51 @@ async function searchGitHubIssues(params) {
1961
2197
  comments: issue.comments,
1962
2198
  reactions: issue.reactions?.total_count || 0,
1963
2199
  }));
2200
+ // Smart fallback suggestions for no results
2201
+ if (cleanIssues.length === 0) {
2202
+ const fallbackSuggestions = [];
2203
+ // Analyze search parameters for specific suggestions
2204
+ if (params.state === 'closed') {
2205
+ fallbackSuggestions.push('• Try state:open or remove state filter');
2206
+ }
2207
+ if (params.author) {
2208
+ fallbackSuggestions.push('• Remove author filter for broader search');
2209
+ fallbackSuggestions.push(`• Use github_search_code to find ${params.author}'s contributions`);
2210
+ }
2211
+ if (params.label) {
2212
+ const labels = Array.isArray(params.label)
2213
+ ? params.label
2214
+ : [params.label];
2215
+ fallbackSuggestions.push(`• Try broader labels or remove label filter`);
2216
+ fallbackSuggestions.push(`• Search for label variations: ${labels.map(l => `"${l}"`).join(', ')}`);
2217
+ }
2218
+ if (params.owner && params.repo) {
2219
+ fallbackSuggestions.push('• Check repository name spelling');
2220
+ fallbackSuggestions.push('• Use github_view_repo_structure to verify repository exists');
2221
+ }
2222
+ else if (params.owner) {
2223
+ fallbackSuggestions.push('• Remove owner filter for global search');
2224
+ fallbackSuggestions.push('• Use github_search_repos to find organization repositories');
2225
+ }
2226
+ if (params.created || params.updated) {
2227
+ fallbackSuggestions.push('• Expand date range or remove date filters');
2228
+ }
2229
+ // Add general alternatives
2230
+ fallbackSuggestions.push('• Try broader search terms');
2231
+ fallbackSuggestions.push('• Use github_search_pull_requests for related development activity');
2232
+ fallbackSuggestions.push('• Use github_search_code to find implementation patterns');
2233
+ return createResult({
2234
+ error: `No issues found for query: "${params.query}"
2235
+
2236
+ Try these alternatives:
2237
+ ${fallbackSuggestions.join('\n')}
2238
+
2239
+ Discovery strategies:
2240
+ • Broader terms: "error" instead of "TypeError"
2241
+ • Remove filters: state, labels, author
2242
+ • Related searches: pull requests, code implementations`,
2243
+ });
2244
+ }
1964
2245
  const searchResult = {
1965
2246
  results: cleanIssues,
1966
2247
  };
@@ -2083,10 +2364,10 @@ function buildGitHubIssuesAPICommand(params) {
2083
2364
 
2084
2365
  // TODO: add PR commeents. e.g, gh pr view <PR_NUMBER_OR_URL_OR_BRANCH> --comments
2085
2366
  const GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME = 'githubSearchPullRequests';
2086
- const DESCRIPTION$3 = `Search pull requests for implementation discovery and code review analysis. Supports filtering by state, review status, branches, and more. Parameters: query (required), owner (optional - GitHub username/org, NOT owner/repo), repo (optional - repository name, use with owner for specific repo), author (optional), assignee (optional), mentions (optional), commenter (optional), involves (optional), reviewedBy (optional), reviewRequested (optional), state (optional), head (optional), base (optional), language (optional), created (optional), updated (optional), mergedAt (optional), closed (optional), draft (optional), checks (optional), merged (optional), review (optional), limit (optional), sort (optional), order (optional).`;
2367
+ 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
2368
  function registerSearchGitHubPullRequestsTool(server) {
2088
2369
  server.registerTool(GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME, {
2089
- description: DESCRIPTION$3,
2370
+ description: DESCRIPTION$4,
2090
2371
  inputSchema: {
2091
2372
  query: z
2092
2373
  .string()
@@ -2095,11 +2376,11 @@ function registerSearchGitHubPullRequestsTool(server) {
2095
2376
  owner: z
2096
2377
  .string()
2097
2378
  .optional()
2098
- .describe('Repository owner/org name only (e.g., "microsoft", "google", NOT "microsoft/vscode"). Use with repo parameter for repository-specific searches.'),
2379
+ .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
2380
  repo: z
2100
2381
  .string()
2101
2382
  .optional()
2102
- .describe('Repository name only (e.g., "vscode", "react", NOT "owner/repo"). Must be used together with owner parameter.'),
2383
+ .describe('Repository name only (e.g., "react", "vscode"). Do NOT include owner prefix. Must be used together with owner parameter.'),
2103
2384
  author: z.string().optional().describe('GitHub username of PR author'),
2104
2385
  assignee: z.string().optional().describe('GitHub username of assignee'),
2105
2386
  mentions: z.string().optional().describe('PRs mentioning this user'),
@@ -2118,10 +2399,7 @@ function registerSearchGitHubPullRequestsTool(server) {
2118
2399
  .optional()
2119
2400
  .describe('PR state. Default: all'),
2120
2401
  head: z.string().optional().describe('Source branch name'),
2121
- base: z
2122
- .string()
2123
- .optional()
2124
- .describe('Target branch name (main, develop, etc.)'),
2402
+ base: z.string().optional().describe('Target branch name'),
2125
2403
  language: z.string().optional().describe('Repository language'),
2126
2404
  created: z
2127
2405
  .string()
@@ -2409,26 +2687,38 @@ function buildGitHubPullRequestsAPICommand(params) {
2409
2687
  * 4. Recent Quality:
2410
2688
  * { stars: ">1000", created: ">2023-01-01", limit: 10 }
2411
2689
  *
2690
+ * RESEARCH & EXPLORATION PATTERNS:
2691
+ *
2692
+ * 1. Topic-based Discovery (HIGHLY RECOMMENDED for unknown projects):
2693
+ * { topic: ["machine-learning", "nlp", "pytorch"], limit: 20 }
2694
+ * { topic: ["kubernetes", "monitoring"], stars: ">100", limit: 15 }
2695
+ *
2696
+ * 2. Exploratory Research Flow:
2697
+ * - Start with topics to discover repositories
2698
+ * - Then use githubViewRepoStructure to understand project layout
2699
+ * - Read README.md, docs/, and configuration files
2700
+ * - Finally use githubSearchCode for specific implementations
2701
+ *
2412
2702
  * AVOID: OR queries + language filter, 5+ filters, multi-word OR
2413
2703
  * TIP: Use limit parameter instead of adding more filters
2414
2704
  */
2415
2705
  const GITHUB_SEARCH_REPOSITORIES_TOOL_NAME = 'githubSearchRepositories';
2416
- const DESCRIPTION$2 = `Search GitHub repositories using GitHub's repository search API with comprehensive filtering.
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
2423
-
2424
- Supported Filters: language, stars (ranges), topics, owner/org, license, dates (created/updated),
2425
- size, forks, community metrics (good-first-issues, help-wanted), archived status, visibility.
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`;
2706
+ const DESCRIPTION$3 = `Search GitHub repositories for project discovery and ecosystem analysis. TOPICS are the most powerful discovery feature.
2707
+
2708
+ TOPIC-DRIVEN DISCOVERY:
2709
+ - Topics reveal ecosystem relationships and quality indicators
2710
+ - Combine topics for targeted discovery: ["framework", "use-case", "language"]
2711
+ - Topics beat keyword searches for unknown project exploration
2712
+
2713
+ PROGRESSIVE SEARCH STRATEGY:
2714
+ - Start with topic combinations for broad discovery
2715
+ - Add quality filters (stars, activity) for refinement
2716
+ - Use multiple separate searches vs complex single queries
2717
+
2718
+ RESEARCH INTEGRATION:
2719
+ - Repository discovery → structure analysis → implementation patterns
2720
+ - Quality assessment via community metrics and activity
2721
+ - Bridge to code search and file analysis tools`;
2432
2722
  /**
2433
2723
  * Extract owner/repo information from various query formats
2434
2724
  */
@@ -2461,21 +2751,21 @@ function extractOwnerRepoFromQuery(query) {
2461
2751
  }
2462
2752
  function registerSearchGitHubReposTool(server) {
2463
2753
  server.registerTool(GITHUB_SEARCH_REPOSITORIES_TOOL_NAME, {
2464
- description: DESCRIPTION$2,
2754
+ description: DESCRIPTION$3,
2465
2755
  inputSchema: {
2466
2756
  query: z
2467
2757
  .string()
2468
2758
  .optional()
2469
- .describe('Search query with AND logic between terms. Multiple words require ALL to be present. Use quotes for exact phrases. Examples: react typescript (both words), "web framework" (exact phrase), "machine learning" python (phrase + word).'),
2759
+ .describe('Search query with AND logic between terms. Multiple words require ALL to be present. Use quotes for exact phrases.'),
2470
2760
  // CORE FILTERS (GitHub CLI flags)
2471
2761
  owner: z
2472
2762
  .union([z.string(), z.array(z.string())])
2473
2763
  .optional()
2474
- .describe('Repository owner or organization name(s). Format: "microsoft" or ["facebook", "google"]. Do NOT use owner/repo format. Dramatically narrows search scope to specific organizations.'),
2764
+ .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
2765
  language: z
2476
2766
  .string()
2477
2767
  .optional()
2478
- .describe('Programming language filter (javascript, python, typescript, go, etc). Filters repositories by primary language. Essential for language-specific searches.'),
2768
+ .describe('Programming language filter. Filters repositories by primary language. Essential for language-specific searches.'),
2479
2769
  stars: z
2480
2770
  .union([
2481
2771
  z.number().int().min(0),
@@ -2484,11 +2774,11 @@ function registerSearchGitHubReposTool(server) {
2484
2774
  .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">1000", ">=500", "<100", "<=50", "10..100", or exact number "50"'),
2485
2775
  ])
2486
2776
  .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). Examples from docs: ">1000", "100..5000", "<100"'),
2777
+ .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
2778
  topic: z
2489
2779
  .union([z.string(), z.array(z.string())])
2490
2780
  .optional()
2491
- .describe('Repository topics filter. Examples: "machine-learning", ["react", "typescript"]. Topics use kebab-case format (machine-learning, web-development).'),
2781
+ .describe('Repository topics filter. Excellent for discovering projects and understanding repository ecosystems. Topics use kebab-case format.'),
2492
2782
  forks: z
2493
2783
  .union([
2494
2784
  z.number().int().min(0),
@@ -2497,7 +2787,7 @@ function registerSearchGitHubReposTool(server) {
2497
2787
  .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">100", ">=50", "<10", "<=5", "10..100", or exact number "5"'),
2498
2788
  ])
2499
2789
  .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). Examples from docs: ">100", "10..100", "<10"'),
2790
+ .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
2791
  // Match CLI parameter name exactly
2502
2792
  'number-topics': z
2503
2793
  .union([
@@ -2507,40 +2797,40 @@ function registerSearchGitHubReposTool(server) {
2507
2797
  .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">5", ">=3", "<10", "<=2", "3..10", or exact number "5"'),
2508
2798
  ])
2509
2799
  .optional()
2510
- .describe('Number of topics filter. Format: ">5" (many topics), ">=3" (at least 3), "<10" (few topics), "1..3" (range), "5" (exact). Well-documented projects typically have 3-10 topics.'),
2800
+ .describe('Number of topics filter. Format: ">5" (many topics), ">=3" (at least 3), "<10" (few topics), "1..3" (range), "5" (exact).'),
2511
2801
  // QUALITY & STATE FILTERS
2512
2802
  license: z
2513
2803
  .union([z.string(), z.array(z.string())])
2514
2804
  .optional()
2515
- .describe('License filter. Examples: "mit", "apache-2.0", ["mit", "apache-2.0"]. Common licenses: mit, apache-2.0, gpl-3.0, bsd-3-clause.'),
2805
+ .describe('License filter.'),
2516
2806
  archived: z
2517
2807
  .boolean()
2518
2808
  .optional()
2519
- .describe('Archive status filter. false (active repos only), true (archived repos only). Use false to find actively maintained projects.'),
2809
+ .describe('Archive status filter. false (active repos only), true (archived repos only).'),
2520
2810
  'include-forks': z
2521
2811
  .enum(['false', 'true', 'only'])
2522
2812
  .optional()
2523
- .describe('Fork inclusion. "false" (exclude forks), "true" (include forks), "only" (forks only). Use "false" for original projects.'),
2813
+ .describe('Fork inclusion. "false" (exclude forks), "true" (include forks), "only" (forks only).'),
2524
2814
  visibility: z
2525
2815
  .enum(['public', 'private', 'internal'])
2526
2816
  .optional()
2527
- .describe('Repository visibility. "public" (open source), "private" (private repos you have access to), "internal" (organization internal).'),
2817
+ .describe('Repository visibility.'),
2528
2818
  // DATE & SIZE FILTERS
2529
2819
  created: z
2530
2820
  .string()
2531
2821
  .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
2822
  .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). Examples from docs: ">2020-01-01", "2023-01-01..2023-12-31"'),
2823
+ .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
2824
  updated: z
2535
2825
  .string()
2536
2826
  .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
2827
  .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). Essential for finding actively maintained projects. Examples from docs: ">2024-01-01", "2022-01-01..2023-12-31"'),
2828
+ .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
2829
  size: z
2540
2830
  .string()
2541
2831
  .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid size format. Use: ">1000", ">=500", "<100", "<=50", "100..1000", or exact number "500"')
2542
2832
  .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). Examples from docs: ">1000", "100..1000", "<100"'),
2833
+ .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
2834
  // COMMUNITY FILTERS - Match CLI parameter names exactly
2545
2835
  'good-first-issues': z
2546
2836
  .union([
@@ -2550,7 +2840,7 @@ function registerSearchGitHubReposTool(server) {
2550
2840
  .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: number, ">5", ">=10", "<20", "<=15", or "5..20"'),
2551
2841
  ])
2552
2842
  .optional()
2553
- .describe('Good first issues count. Format: ">5" (many beginner issues), "1..10" (some beginner issues). Perfect for finding beginner-friendly open source projects.'),
2843
+ .describe('Good first issues count. Format: ">5" (many beginner issues), "1..10" (some beginner issues).'),
2554
2844
  'help-wanted-issues': z
2555
2845
  .union([
2556
2846
  z.number().int().min(0),
@@ -2559,7 +2849,7 @@ function registerSearchGitHubReposTool(server) {
2559
2849
  .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: number, ">5", ">=10", "<20", "<=15", or "5..20"'),
2560
2850
  ])
2561
2851
  .optional()
2562
- .describe('Help wanted issues count. Format: ">10" (many help wanted), "1..5" (some help wanted). Great for finding projects actively seeking contributors.'),
2852
+ .describe('Help wanted issues count. Format: ">10" (many help wanted), "1..5" (some help wanted).'),
2563
2853
  followers: z
2564
2854
  .union([
2565
2855
  z.number().int().min(0),
@@ -2568,12 +2858,15 @@ function registerSearchGitHubReposTool(server) {
2568
2858
  .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">1000", ">=500", "<100", "<=50", "100..1000", or exact number "500"'),
2569
2859
  ])
2570
2860
  .optional()
2571
- .describe('Repository owner followers count. Format: ">1000" (popular developers), ">=500" (established developers), "<100" (smaller developers), "100..1000" (range). Indicates developer/org reputation.'),
2572
- // SEARCH SCOPE
2861
+ .describe('Repository owner followers count. Format: ">1000" (popular developers), ">=500" (established developers), "<100" (smaller developers), "100..1000" (range).'),
2862
+ // SEARCH SCOPE - Match CLI exactly
2573
2863
  match: z
2574
- .enum(['name', 'description', 'readme'])
2864
+ .union([
2865
+ z.enum(['name', 'description', 'readme']),
2866
+ z.array(z.enum(['name', 'description', 'readme'])),
2867
+ ])
2575
2868
  .optional()
2576
- .describe('Search scope. "name" (repository names only), "description" (descriptions only), "readme" (README content). Default searches all fields.'),
2869
+ .describe('Search scope. "name" (repository names only), "description" (descriptions only), "readme" (README content). Can be single value or array.'),
2577
2870
  // SORTING & LIMITS - Match CLI defaults exactly
2578
2871
  sort: z
2579
2872
  .enum([
@@ -2585,12 +2878,12 @@ function registerSearchGitHubReposTool(server) {
2585
2878
  ])
2586
2879
  .optional()
2587
2880
  .default('best-match')
2588
- .describe('Sort criteria. "stars" (popularity), "updated" (recent activity), "forks" (community engagement), "help-wanted-issues" (contribution opportunities), "best-match" (relevance).'),
2881
+ .describe('Sort criteria.'),
2589
2882
  order: z
2590
2883
  .enum(['asc', 'desc'])
2591
2884
  .optional()
2592
2885
  .default('desc')
2593
- .describe('Sort order direction. "desc" (descending, highest first), "asc" (ascending, lowest first). Default is descending for most useful results.'),
2886
+ .describe('Sort order direction.'),
2594
2887
  limit: z
2595
2888
  .number()
2596
2889
  .int()
@@ -2598,7 +2891,7 @@ function registerSearchGitHubReposTool(server) {
2598
2891
  .max(100)
2599
2892
  .optional()
2600
2893
  .default(30)
2601
- .describe('Maximum number of repositories to return (1-100). Default: 30. Higher values may increase response time.'),
2894
+ .describe('Maximum number of repositories to return (1-100).'),
2602
2895
  },
2603
2896
  annotations: {
2604
2897
  title: 'GitHub Repository Search',
@@ -2637,34 +2930,87 @@ function registerSearchGitHubReposTool(server) {
2637
2930
  enhancedArgs.forks;
2638
2931
  if (!hasPrimaryFilter) {
2639
2932
  return createResult({
2640
- error: SUGGESTIONS.REPO_SEARCH_PRIMARY_FILTER,
2933
+ error: `Repository search requires at least one filter. Try these patterns:
2934
+ • Topic discovery: { topic: ["react", "typescript"] }
2935
+ • Quality search: { stars: ">1000", language: "javascript" }
2936
+ • Organization: { owner: "microsoft", language: "python" }
2937
+ • Recent projects: { created: ">2023-01-01", stars: ">100" }`,
2641
2938
  });
2642
2939
  }
2643
2940
  // First attempt: Search with current parameters
2644
2941
  const result = await searchGitHubRepos(enhancedArgs);
2942
+ if (result.isError) {
2943
+ const errorMsg = result.content?.[0]?.text || '';
2944
+ // Smart fallbacks based on error type
2945
+ if (errorMsg.includes('rate limit')) {
2946
+ return createResult({
2947
+ error: `GitHub API rate limit. Smart alternatives:
2948
+ • Try npm package search for package discovery
2949
+ • Use broader filters (remove stars/forks constraints)
2950
+ • Search fewer organizations at once
2951
+ • Wait 5-10 minutes and retry`,
2952
+ });
2953
+ }
2954
+ if (errorMsg.includes('authentication')) {
2955
+ return createResult({
2956
+ error: `Authentication required. Quick fix:
2957
+ 1. Run: gh auth login
2958
+ 2. For private repos: use api_status_check to verify access
2959
+ 3. Public repos should work without auth - check query`,
2960
+ });
2961
+ }
2962
+ return result; // Return original error for other cases
2963
+ }
2964
+ // Check if we got results
2965
+ const resultData = JSON.parse(result.content[0].text);
2966
+ const hasResults = resultData.total_count > 0;
2967
+ // Smart fallback strategies for no results
2968
+ if (!hasResults) {
2969
+ const fallbackSuggestions = [];
2970
+ if (enhancedArgs.query) {
2971
+ fallbackSuggestions.push('• Try broader search terms or remove query filter');
2972
+ }
2973
+ if (enhancedArgs.language) {
2974
+ fallbackSuggestions.push('• Remove language filter for broader discovery');
2975
+ }
2976
+ if (enhancedArgs.stars || enhancedArgs.forks) {
2977
+ fallbackSuggestions.push('• Lower quality thresholds (stars/forks)');
2978
+ }
2979
+ if (!enhancedArgs.topic) {
2980
+ fallbackSuggestions.push('• Try topic-based search: { topic: ["web", "api"] }');
2981
+ }
2982
+ if (enhancedArgs.owner) {
2983
+ fallbackSuggestions.push('• Search without owner filter for global discovery');
2984
+ fallbackSuggestions.push('• Use npm package search if looking for packages');
2985
+ }
2986
+ return createResult({
2987
+ error: `No repositories found. Try these alternatives:
2988
+ ${fallbackSuggestions.join('\n')}
2989
+
2990
+ Discovery patterns:
2991
+ • Topic exploration: { topic: ["react"] }
2992
+ • Quality discovery: { stars: ">100", language: "python" }
2993
+ • Recent activity: { updated: ">2024-01-01" }`,
2994
+ });
2995
+ }
2645
2996
  // Fallback for private repositories: If no results and owner is specified, try with private visibility
2646
- if (!result.isError) {
2647
- const resultData = JSON.parse(result.content[0].text);
2648
- if (resultData.total === 0 &&
2649
- enhancedArgs.owner &&
2650
- !enhancedArgs.visibility) {
2651
- // Try searching with private visibility for organization repos
2652
- const privateSearchArgs = {
2653
- ...enhancedArgs,
2654
- visibility: 'private',
2655
- };
2656
- const privateResult = await searchGitHubRepos(privateSearchArgs);
2657
- if (!privateResult.isError) {
2658
- const privateData = JSON.parse(privateResult.content[0].text);
2659
- if (privateData.total > 0) {
2660
- // Return private results with note
2661
- return createResult({
2662
- data: {
2663
- ...privateData,
2664
- note: 'Found results in private repositories within the specified organization.',
2665
- },
2666
- });
2667
- }
2997
+ if (enhancedArgs.owner && !enhancedArgs.visibility) {
2998
+ // Try searching with private visibility for organization repos
2999
+ const privateSearchArgs = {
3000
+ ...enhancedArgs,
3001
+ visibility: 'private',
3002
+ };
3003
+ const privateResult = await searchGitHubRepos(privateSearchArgs);
3004
+ if (!privateResult.isError) {
3005
+ const privateData = JSON.parse(privateResult.content[0].text);
3006
+ if (privateData.total_count > 0) {
3007
+ // Return private results with note
3008
+ return createResult({
3009
+ data: {
3010
+ ...privateData,
3011
+ note: 'Found results in private repositories within the specified organization.',
3012
+ },
3013
+ });
2668
3014
  }
2669
3015
  }
2670
3016
  }
@@ -2819,17 +3165,300 @@ function buildGitHubReposSearchCommand(params) {
2819
3165
  // SEARCH SCOPE
2820
3166
  addArg('match', 'match');
2821
3167
  // SORTING AND LIMITS
2822
- addArg('limit', 'limit');
2823
- addArg('order', 'order');
2824
3168
  const sortBy = params.sort || 'best-match';
2825
3169
  if (sortBy !== 'best-match') {
2826
3170
  args.push(`--sort=${sortBy}`);
2827
3171
  }
3172
+ addArg('order', 'order');
3173
+ // Always add limit with default of 30
3174
+ const limit = params.limit || 30;
3175
+ args.push(`--limit=${limit}`);
2828
3176
  return { command: 'search', args };
2829
3177
  }
2830
3178
 
3179
+ const GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME = 'githubViewRepoStructure';
3180
+ const DESCRIPTION$2 = `Explore GitHub repository structure and validate repository access. ESSENTIAL for understanding project organization.
3181
+
3182
+ REPOSITORY VALIDATION:
3183
+ - Verify repository existence and accessibility
3184
+ - Navigate from root to understand project layout
3185
+ - Identify key directories and file patterns
3186
+
3187
+ PROJECT UNDERSTANDING:
3188
+ - Discover configuration files, documentation, source structure
3189
+ - Validate paths before accessing specific files
3190
+ - Understand architecture and organization patterns
3191
+
3192
+ WORKFLOW INTEGRATION:
3193
+ - First step after repository discovery
3194
+ - Guides subsequent file access and code search
3195
+ - Prevents "file not found" errors through validation
3196
+ - Essential for comprehensive repository analysis`;
3197
+ function registerViewRepositoryStructureTool(server) {
3198
+ server.registerTool(GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME, {
3199
+ description: DESCRIPTION$2,
3200
+ inputSchema: {
3201
+ owner: z
3202
+ .string()
3203
+ .min(1)
3204
+ .max(100)
3205
+ .regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/, 'Invalid GitHub username/org format')
3206
+ .describe('Repository owner/organization name (e.g., "facebook", "microsoft"). Do NOT include repository name.'),
3207
+ repo: z
3208
+ .string()
3209
+ .min(1)
3210
+ .max(100)
3211
+ .regex(/^[a-zA-Z0-9._-]+$/, 'Invalid repository name format')
3212
+ .describe(`Repository name under a organization. `),
3213
+ branch: z
3214
+ .string()
3215
+ .min(1)
3216
+ .max(255)
3217
+ .regex(/^[^\s]+$/, 'Branch name cannot contain spaces')
3218
+ .describe('Branch name (e.g., "main", "master", "develop"). Tool will automatically try default branch if specified branch is not found.'),
3219
+ path: z
3220
+ .string()
3221
+ .optional()
3222
+ .default('')
3223
+ .refine(path => !path.includes('..'), 'Path traversal not allowed')
3224
+ .refine(path => path.length <= 500, 'Path too long')
3225
+ .describe('Directory path within repository (e.g., "src", "docs", "src/components"). Leave empty for root directory. Do NOT start with slash.'),
3226
+ },
3227
+ annotations: {
3228
+ title: 'GitHub Repository Explorer',
3229
+ readOnlyHint: true,
3230
+ destructiveHint: false,
3231
+ idempotentHint: true,
3232
+ openWorldHint: true,
3233
+ },
3234
+ }, async (args) => {
3235
+ try {
3236
+ const result = await viewRepositoryStructure(args);
3237
+ return result;
3238
+ }
3239
+ catch (error) {
3240
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
3241
+ return createResult({
3242
+ error: `Failed to explore repository. ${errorMessage}. Verify repository exists and is accessible`,
3243
+ });
3244
+ }
3245
+ });
3246
+ }
3247
+ /**
3248
+ * Views the structure of a GitHub repository at a specific path.
3249
+ * Optimized for code analysis workflows with smart defaults and clear errors.
3250
+ */
3251
+ async function viewRepositoryStructure(params) {
3252
+ const cacheKey = generateCacheKey('gh-repo-structure', params);
3253
+ return withCache(cacheKey, async () => {
3254
+ const { owner, repo, branch, path = '' } = params;
3255
+ try {
3256
+ // Clean up path
3257
+ const cleanPath = path.startsWith('/') ? path.substring(1) : path;
3258
+ // Try the requested branch first, then fallback to main/master
3259
+ const branchesToTry = await getSmartBranchFallback(owner, repo, branch);
3260
+ let items = [];
3261
+ let usedBranch = branch;
3262
+ let lastError = null;
3263
+ let attemptCount = 0;
3264
+ const maxAttempts = branchesToTry.length;
3265
+ const triedBranches = [];
3266
+ for (const tryBranch of branchesToTry) {
3267
+ if (attemptCount >= maxAttempts)
3268
+ break;
3269
+ attemptCount++;
3270
+ triedBranches.push(tryBranch);
3271
+ try {
3272
+ const apiPath = `/repos/${owner}/${repo}/contents/${cleanPath}?ref=${tryBranch}`;
3273
+ const result = await executeGitHubCommand('api', [apiPath], {
3274
+ cache: false,
3275
+ });
3276
+ if (!result.isError) {
3277
+ const execResult = JSON.parse(result.content[0].text);
3278
+ const apiItems = execResult.result;
3279
+ items = Array.isArray(apiItems) ? apiItems : [apiItems];
3280
+ usedBranch = tryBranch;
3281
+ break;
3282
+ }
3283
+ else {
3284
+ lastError = new Error(result.content[0].text);
3285
+ }
3286
+ }
3287
+ catch (error) {
3288
+ lastError = error instanceof Error ? error : new Error(String(error));
3289
+ continue;
3290
+ }
3291
+ }
3292
+ if (items.length === 0) {
3293
+ // Check repository existence only after content fetch fails
3294
+ const repoCheckResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
3295
+ cache: false,
3296
+ });
3297
+ if (repoCheckResult.isError) {
3298
+ const repoErrorMsg = repoCheckResult.content[0].text;
3299
+ if (repoErrorMsg.includes('404')) {
3300
+ return createResult({
3301
+ error: `Repository "${owner}/${repo}" not found. It might have been deleted, renamed, or made private. Use github_search_code to find current location.`,
3302
+ });
3303
+ }
3304
+ else if (repoErrorMsg.includes('403')) {
3305
+ return createResult({
3306
+ error: `Repository "${owner}/${repo}" exists but access is denied. Repository might be private or archived. Use api_status_check to verify permissions.`,
3307
+ });
3308
+ }
3309
+ }
3310
+ const errorMsg = lastError?.message || 'Unknown error';
3311
+ if (errorMsg.includes('404') || errorMsg.includes('Not Found')) {
3312
+ if (path) {
3313
+ const searchSuggestion = await suggestPathSearchFallback(owner, path);
3314
+ return createResult({
3315
+ error: `Path "${path}" not found in any branch (tried: ${triedBranches.join(', ')}).${searchSuggestion}`,
3316
+ });
3317
+ }
3318
+ else {
3319
+ return createResult({
3320
+ 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.`,
3321
+ });
3322
+ }
3323
+ }
3324
+ else if (errorMsg.includes('403') || errorMsg.includes('Forbidden')) {
3325
+ return createResult({
3326
+ 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.`,
3327
+ });
3328
+ }
3329
+ else {
3330
+ const searchSuggestion = path
3331
+ ? await suggestPathSearchFallback(owner, path)
3332
+ : '';
3333
+ return createResult({
3334
+ error: `Failed to access "${owner}/${repo}": ${errorMsg}. Check network connection and repository permissions.${searchSuggestion}`,
3335
+ });
3336
+ }
3337
+ }
3338
+ // Limit total items to 100 for efficiency
3339
+ const limitedItems = items.slice(0, 100);
3340
+ // Sort: directories first, then alphabetically
3341
+ limitedItems.sort((a, b) => {
3342
+ if (a.type !== b.type) {
3343
+ return a.type === 'dir' ? -1 : 1;
3344
+ }
3345
+ return a.name.localeCompare(b.name);
3346
+ });
3347
+ // Create simplified, token-efficient structure
3348
+ const files = limitedItems
3349
+ .filter(item => item.type === 'file')
3350
+ .map(item => ({
3351
+ name: item.name,
3352
+ size: item.size,
3353
+ url: item.path, // Use path for fetching
3354
+ }));
3355
+ const folders = limitedItems
3356
+ .filter(item => item.type === 'dir')
3357
+ .map(item => ({
3358
+ name: item.name,
3359
+ url: item.path, // Use path for browsing
3360
+ }));
3361
+ return createResult({
3362
+ data: {
3363
+ repository: `${owner}/${repo}`,
3364
+ branch: usedBranch,
3365
+ path: cleanPath || '/',
3366
+ githubBasePath: `https://api.github.com/repos/${owner}/${repo}/contents/`,
3367
+ files: {
3368
+ count: files.length,
3369
+ files: files,
3370
+ },
3371
+ folders: {
3372
+ count: folders.length,
3373
+ folders: folders,
3374
+ },
3375
+ },
3376
+ });
3377
+ }
3378
+ catch (error) {
3379
+ const errorMessage = error instanceof Error ? error.message : String(error);
3380
+ return createResult({
3381
+ error: `Failed to access repository "${owner}/${repo}": ${errorMessage}. Verify repository name, permissions, and network connection.`,
3382
+ });
3383
+ }
3384
+ });
3385
+ }
3386
+ /**
3387
+ * Smart branch detection with automatic fallback to common branch names.
3388
+ * Now includes more comprehensive branch detection and better error handling.
3389
+ */
3390
+ async function getSmartBranchFallback(owner, repo, requestedBranch) {
3391
+ const branches = new Set([requestedBranch]);
3392
+ try {
3393
+ // Try to get repository info to find default branch
3394
+ const repoInfoResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
3395
+ cache: false,
3396
+ });
3397
+ if (!repoInfoResult.isError) {
3398
+ const execResult = JSON.parse(repoInfoResult.content[0].text);
3399
+ const repoData = execResult.result;
3400
+ const defaultBranch = repoData.default_branch;
3401
+ if (defaultBranch) {
3402
+ branches.add(defaultBranch);
3403
+ }
3404
+ }
3405
+ }
3406
+ catch {
3407
+ // If we can't get repo info, proceed with standard fallbacks
3408
+ }
3409
+ // Add only main/master as fallback branches
3410
+ const commonBranches = ['main', 'master'];
3411
+ commonBranches.forEach(branch => branches.add(branch));
3412
+ // Convert Set back to array, with requested branch first
3413
+ const branchesArray = Array.from(branches);
3414
+ branchesArray.sort((a, b) => {
3415
+ if (a === requestedBranch)
3416
+ return -1;
3417
+ if (b === requestedBranch)
3418
+ return 1;
3419
+ return 0;
3420
+ });
3421
+ return branchesArray;
3422
+ }
3423
+ // Helper function to suggest path search strategy
3424
+ async function suggestPathSearchFallback(owner, path) {
3425
+ try {
3426
+ // Extract last path segment and try to find in same organization
3427
+ const pathSegment = path.split('/').pop() || path;
3428
+ const searchResult = await executeGitHubCommand('api', [
3429
+ `/search/code?q=${encodeURIComponent(pathSegment)}+in:path+org:${owner}`,
3430
+ ], { cache: false });
3431
+ if (!searchResult.isError) {
3432
+ const results = JSON.parse(searchResult.content[0].text);
3433
+ if (results.total_count > 0) {
3434
+ const firstMatch = results.items[0];
3435
+ 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}"`;
3436
+ }
3437
+ }
3438
+ }
3439
+ catch {
3440
+ // Fallback to generic message if search fails
3441
+ }
3442
+ return ` Try these searches:\n1. github_search_code with query="${path.split('/').pop()}" owner="${owner}"\n2. github_search_code with query="path:${path}"`;
3443
+ }
3444
+
2831
3445
  const NPM_PACKAGE_SEARCH_TOOL_NAME = 'npmPackageSearch';
2832
- const DESCRIPTION$1 = `Search NPM packages with fuzzy matching. Supports multiple search terms and aggregates results. Use functional keywords like "react hooks", "auth", or "testing". Parameters: queries (required, string or array), searchLimit (optional).`;
3446
+ const DESCRIPTION$1 = `Search NPM packages by functionality keywords. PRIMARY ENTRY POINT for package-related queries.
3447
+
3448
+ PACKAGE-FIRST STRATEGY:
3449
+ - Start here when users mention: libraries, dependencies, installations, alternatives
3450
+ - Use broad functional terms for discovery (not exact package names)
3451
+ - Bridge to GitHub tools via repository URLs from results
3452
+
3453
+ SEARCH APPROACH:
3454
+ - Single functional terms work best for discovery
3455
+ - Multiple searches for different aspects/use-cases
3456
+ - Reveals ecosystem alternatives and quality indicators
3457
+
3458
+ INTEGRATION WORKFLOW:
3459
+ - Package Discovery → Repository Analysis → Implementation Patterns
3460
+ - npmPackageSearch → npmViewPackage → GitHub repository tools
3461
+ - Compare alternatives by searching different functional terms`;
2833
3462
  const MAX_DESCRIPTION_LENGTH = 100;
2834
3463
  const MAX_KEYWORDS = 10;
2835
3464
  function registerNpmSearchTool(server) {
@@ -2838,7 +3467,7 @@ function registerNpmSearchTool(server) {
2838
3467
  inputSchema: {
2839
3468
  queries: z
2840
3469
  .union([z.string(), z.array(z.string())])
2841
- .describe('Search terms for packages. Use functionality keywords: "react hooks", "cli tool", "testing"'),
3470
+ .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
3471
  searchLimit: z
2843
3472
  .number()
2844
3473
  .int()
@@ -2864,28 +3493,136 @@ function registerNpmSearchTool(server) {
2864
3493
  const allPackages = [];
2865
3494
  // Search for each query term
2866
3495
  for (const query of queries) {
2867
- const result = await executeNpmCommand('search', [query, `--searchlimit=${searchLimit}`, '--json'], { cache: true });
2868
- if (!result.isError && result.content?.[0]?.text) {
2869
- const packages = parseNpmSearchOutput(result.content[0].text);
2870
- allPackages.push(...packages);
3496
+ try {
3497
+ const result = await executeNpmCommand('search', [query, `--searchlimit=${searchLimit}`, '--json'], { cache: true });
3498
+ if (!result.isError && result.content?.[0]?.text) {
3499
+ const packages = parseNpmSearchOutput(result.content[0].text);
3500
+ allPackages.push(...packages);
3501
+ }
3502
+ else if (result.isError) {
3503
+ // Individual query failures are handled silently, continue with others
3504
+ }
3505
+ }
3506
+ catch (queryError) {
3507
+ // Continue with other queries even if one fails
2871
3508
  }
2872
3509
  }
2873
3510
  const deduplicatedPackages = deduplicatePackages(allPackages);
2874
3511
  if (deduplicatedPackages.length > 0) {
3512
+ const { nextSteps } = getToolSuggestions(TOOL_NAMES.NPM_PACKAGE_SEARCH, { hasResults: true });
3513
+ const hints = [];
3514
+ if (nextSteps.length > 0) {
3515
+ hints.push('Next steps:');
3516
+ nextSteps.forEach(({ tool, reason }) => {
3517
+ hints.push(`• Use ${tool} ${reason}`);
3518
+ });
3519
+ }
2875
3520
  return createResult({
2876
3521
  data: {
2877
3522
  total_count: deduplicatedPackages.length,
2878
3523
  results: deduplicatedPackages,
3524
+ hints: hints.length > 0 ? hints : undefined,
2879
3525
  },
2880
3526
  });
2881
3527
  }
3528
+ // Smart fallback suggestions based on query patterns
3529
+ const hasSpecificTerms = queries.some(q => q.includes('-') || q.includes('@') || q.length > 15);
3530
+ const hasFrameworkTerms = queries.some(q => ['react', 'vue', 'angular', 'express', 'fastify'].some(fw => q.toLowerCase().includes(fw)));
3531
+ let fallbackSuggestions = [
3532
+ '• Try broader functional terms: "testing" instead of "jest-unit-test"',
3533
+ '• Remove version numbers or specific constraints',
3534
+ '• Use single keywords: "http" instead of "http-client-library"',
3535
+ ];
3536
+ if (hasSpecificTerms) {
3537
+ fallbackSuggestions = [
3538
+ '• Use simpler terms: "validation" instead of "schema-validation-library"',
3539
+ '• Try category terms: "database", "testing", "auth"',
3540
+ ...fallbackSuggestions.slice(1),
3541
+ ];
3542
+ }
3543
+ if (hasFrameworkTerms) {
3544
+ fallbackSuggestions.unshift('• Try specific framework searches: "react hooks", "vue components"');
3545
+ }
3546
+ // Add GitHub integration suggestions
3547
+ fallbackSuggestions.push('• Use github_search_repos with topic filters for project discovery');
3548
+ fallbackSuggestions.push('• Check npm registry status: https://status.npmjs.org');
3549
+ const { fallback } = getToolSuggestions(TOOL_NAMES.NPM_PACKAGE_SEARCH, {
3550
+ errorType: 'no_results',
3551
+ });
3552
+ const toolSuggestions = createToolSuggestion(TOOL_NAMES.NPM_PACKAGE_SEARCH, fallback);
2882
3553
  return createResult({
2883
- error: createNoResultsError('packages'),
3554
+ error: getErrorWithSuggestion({
3555
+ baseError: createNpmPackageNotFoundError(queries.join(', ')),
3556
+ suggestion: [fallbackSuggestions.join('\n'), toolSuggestions],
3557
+ }),
2884
3558
  });
2885
3559
  }
2886
3560
  catch (error) {
3561
+ const errorMsg = error instanceof Error ? error.message : String(error);
3562
+ // Network/connectivity issues
3563
+ if (errorMsg.includes('network') ||
3564
+ errorMsg.includes('timeout') ||
3565
+ errorMsg.includes('ENOTFOUND')) {
3566
+ const { fallback } = getToolSuggestions(TOOL_NAMES.NPM_PACKAGE_SEARCH, { });
3567
+ return createResult({
3568
+ error: getErrorWithSuggestion({
3569
+ baseError: ERROR_MESSAGES.NPM_CONNECTION_FAILED,
3570
+ suggestion: [
3571
+ '• Check internet connection and npm registry status',
3572
+ '• Try fewer search terms to reduce load',
3573
+ '• Retry in a few moments',
3574
+ createToolSuggestion(TOOL_NAMES.NPM_PACKAGE_SEARCH, fallback),
3575
+ ],
3576
+ }),
3577
+ });
3578
+ }
3579
+ // NPM CLI issues
3580
+ if (errorMsg.includes('command not found') ||
3581
+ errorMsg.includes('npm')) {
3582
+ const { fallback } = getToolSuggestions(TOOL_NAMES.NPM_PACKAGE_SEARCH, { });
3583
+ return createResult({
3584
+ error: getErrorWithSuggestion({
3585
+ baseError: ERROR_MESSAGES.NPM_CLI_ERROR,
3586
+ suggestion: [
3587
+ '• Verify NPM installation: npm --version',
3588
+ '• Update NPM: npm install -g npm@latest',
3589
+ '• Check PATH environment variable',
3590
+ createToolSuggestion(TOOL_NAMES.NPM_PACKAGE_SEARCH, fallback),
3591
+ ],
3592
+ }),
3593
+ });
3594
+ }
3595
+ // Permission/auth issues
3596
+ if (errorMsg.includes('permission') ||
3597
+ errorMsg.includes('403') ||
3598
+ errorMsg.includes('401')) {
3599
+ const { fallback } = getToolSuggestions(TOOL_NAMES.NPM_PACKAGE_SEARCH, { errorType: 'access_denied' });
3600
+ return createResult({
3601
+ error: getErrorWithSuggestion({
3602
+ baseError: ERROR_MESSAGES.NPM_PERMISSION_ERROR,
3603
+ suggestion: [
3604
+ '• Check npm login status: npm whoami',
3605
+ '• Use public registry search without auth',
3606
+ '• Verify npm registry configuration',
3607
+ createToolSuggestion(TOOL_NAMES.NPM_PACKAGE_SEARCH, fallback),
3608
+ ],
3609
+ }),
3610
+ });
3611
+ }
3612
+ const { fallback } = getToolSuggestions(TOOL_NAMES.NPM_PACKAGE_SEARCH, {
3613
+ });
2887
3614
  return createResult({
2888
- error: createSearchFailedError('packages'),
3615
+ error: getErrorWithSuggestion({
3616
+ baseError: ERROR_MESSAGES.PACKAGE_SEARCH_FAILED,
3617
+ suggestion: [
3618
+ `Error details: ${errorMsg}`,
3619
+ '',
3620
+ 'Fallback strategies:',
3621
+ '• Check npm status and retry',
3622
+ '• Use broader search terms',
3623
+ createToolSuggestion(TOOL_NAMES.NPM_PACKAGE_SEARCH, fallback),
3624
+ ],
3625
+ }),
2889
3626
  });
2890
3627
  }
2891
3628
  });
@@ -2937,7 +3674,25 @@ function parseNpmSearchOutput(output) {
2937
3674
  }
2938
3675
 
2939
3676
  const NPM_VIEW_PACKAGE_TOOL_NAME = 'npmViewPackage';
2940
- const DESCRIPTION = `View detailed NPM package information including repository URL, exports, version history, dependencies, and download stats. Returns optimized metadata for code navigation. Parameters: packageName (required).`;
3677
+ const DESCRIPTION = `Analyze NPM packages for repository discovery and dependency insights. BRIDGE to GitHub ecosystem.
3678
+
3679
+ PACKAGE ANALYSIS CAPABILITIES:
3680
+ - Repository URL discovery for GitHub exploration
3681
+ - Version history and release patterns
3682
+ - Export analysis for implementation understanding
3683
+ - Dependency metadata for ecosystem mapping
3684
+
3685
+ GITHUB INTEGRATION:
3686
+ - Provides repository links for GitHub repository tools
3687
+ - Connects package metadata to source code analysis
3688
+ - Enables package-to-implementation research workflows
3689
+ - Essential for dependency and alternative evaluation
3690
+
3691
+ USE CASES:
3692
+ - Repository discovery from package names
3693
+ - Version analysis and security assessment
3694
+ - Export structure for integration planning
3695
+ - Dependency research and ecosystem exploration`;
2941
3696
  function registerNpmViewPackageTool(server) {
2942
3697
  server.registerTool(NPM_VIEW_PACKAGE_TOOL_NAME, {
2943
3698
  description: DESCRIPTION,
@@ -2945,7 +3700,7 @@ function registerNpmViewPackageTool(server) {
2945
3700
  packageName: z
2946
3701
  .string()
2947
3702
  .min(1)
2948
- .describe('NPM package name (e.g., "react", "express", "@types/node")'),
3703
+ .describe('NPM package name (e.g., "react", "express", "@types/node"). Include @ prefix for scoped packages.'),
2949
3704
  },
2950
3705
  annotations: {
2951
3706
  title: 'NPM Package Analyzer',
@@ -2968,18 +3723,87 @@ function registerNpmViewPackageTool(server) {
2968
3723
  }
2969
3724
  catch (error) {
2970
3725
  const errorMessage = error.message || '';
2971
- if (errorMessage.includes('not found')) {
3726
+ // Package not found with smart discovery
3727
+ if (errorMessage.includes('not found') ||
3728
+ errorMessage.includes('404')) {
3729
+ const packageName = args.packageName;
3730
+ const suggestions = [];
3731
+ // Check for common naming patterns
3732
+ if (packageName.includes('_')) {
3733
+ suggestions.push(`• Try with dashes: "${packageName.replace(/_/g, '-')}"`);
3734
+ }
3735
+ if (packageName.includes('-')) {
3736
+ suggestions.push(`• Try without dashes: "${packageName.replace(/-/g, '')}"`);
3737
+ }
3738
+ if (!packageName.startsWith('@') && packageName.includes('/')) {
3739
+ suggestions.push(`• Try scoped package: "@${packageName}"`);
3740
+ }
3741
+ if (packageName.startsWith('@')) {
3742
+ suggestions.push(`• Try without scope: "${packageName.split('/')[1]}"`);
3743
+ }
3744
+ // Add discovery alternatives
3745
+ suggestions.push('• Use npm_package_search for discovery');
3746
+ suggestions.push('• Use github_search_repos to find source repository');
3747
+ suggestions.push('• Check exact spelling on npmjs.com');
3748
+ return createResult({
3749
+ error: `Package "${packageName}" not found on NPM registry.
3750
+
3751
+ Try these alternatives:
3752
+ ${suggestions.join('\n')}
3753
+
3754
+ Discovery workflow:
3755
+ 1. Use npm_package_search with functional terms
3756
+ 2. Use github_search_repos for related projects
3757
+ 3. Verify exact package name on npmjs.com`,
3758
+ });
3759
+ }
3760
+ // Network issues with fallback strategies
3761
+ if (errorMessage.includes('network') ||
3762
+ errorMessage.includes('timeout') ||
3763
+ errorMessage.includes('ENOTFOUND')) {
3764
+ return createResult({
3765
+ error: `NPM registry connection failed. Alternative strategies:
3766
+ • Check internet connection and npm registry status
3767
+ • Use github_search_repos to find package repository
3768
+ • Try npm_package_search for broader discovery
3769
+ • Visit https://npmjs.com/${args.packageName} directly
3770
+ • Retry in a few moments`,
3771
+ });
3772
+ }
3773
+ // NPM CLI issues
3774
+ if (errorMessage.includes('command not found') ||
3775
+ errorMessage.includes('npm')) {
2972
3776
  return createResult({
2973
- error: 'Package not found. Check spelling and use exact package name from npm',
3777
+ error: `NPM CLI issue. Quick fixes:
3778
+ • Verify NPM installation: npm --version
3779
+ • Update NPM: npm install -g npm@latest
3780
+ • Use github_search_repos to find package repository
3781
+ • Check PATH environment variable
3782
+ • Try web interface: https://npmjs.com/${args.packageName}`,
2974
3783
  });
2975
3784
  }
2976
- if (errorMessage.includes('network')) {
3785
+ // Permission/registry issues
3786
+ if (errorMessage.includes('permission') ||
3787
+ errorMessage.includes('403') ||
3788
+ errorMessage.includes('401')) {
2977
3789
  return createResult({
2978
- error: 'Network error. Check internet connection and try again',
3790
+ error: `NPM registry access issue. Try these solutions:
3791
+ • Check npm configuration: npm config get registry
3792
+ • Use public registry: npm config set registry https://registry.npmjs.org/
3793
+ • Try github_search_repos for package source code
3794
+ • Visit package page: https://npmjs.com/${args.packageName}`,
2979
3795
  });
2980
3796
  }
3797
+ // Generic error with comprehensive fallbacks
2981
3798
  return createResult({
2982
- error: 'Failed to fetch package information. Try again or check npm status',
3799
+ error: `Failed to fetch package "${args.packageName}": ${errorMessage}
3800
+
3801
+ Fallback strategies:
3802
+ • Use npm_package_search for similar packages
3803
+ • Use github_search_repos with package name as query
3804
+ • Check package on web: https://npmjs.com/${args.packageName}
3805
+ • Verify npm status: https://status.npmjs.org
3806
+ • Try alternative package managers (yarn info, pnpm view)`,
2983
3807
  });
2984
3808
  }
2985
3809
  });
@@ -3090,138 +3914,120 @@ async function viewNpmPackage(packageName) {
3090
3914
  });
3091
3915
  }
3092
3916
 
3093
- const PROMPT_SYSTEM_PROMPT = `You are an expert code research assistant that knows how to understand users needs and search for the right information
3094
- using gh cli for github and npm cli for packages.
3095
-
3096
- CRITICAL SEARCH PRINCIPLES:
3097
-
3098
- ## 1. PROGRESSIVE SEARCH STRATEGY (MOST IMPORTANT):
3099
- a) START BROAD: Begin with simple, general terms (1-2 words max)
3100
- b) ANALYZE RESULTS: Learn from what you find (repo names, owners, common patterns)
3101
- c) REFINE GRADUALLY: Add filters only after understanding the landscape
3102
- d) MULTIPLE ANGLES: Try different search terms if first approach yields no results
3103
-
3104
- ## 2. SEARCH PROGRESSION EXAMPLES:
3105
- - User asks about "React state management"
3106
- 1st search: "state" or "redux" (BROAD)
3107
- 2nd search: "react state" with language:javascript (REFINED)
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
3122
-
3123
- ## 4. SMART FILTER USAGE:
3124
- - NO FILTERS on first search (unless user specifies)
3125
- - Add ONE filter at a time based on results
3126
- - Common progression: query +language → +stars → +owner
3127
- - Reserve complex filters for final refinement
3128
-
3129
- ## 5. TOOL SYNERGY:
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:
3136
- - Conduct COMPREHENSIVE research with multiple searches
3137
- - Learn from each search to improve the next
3138
- - Provide context about your search strategy
3139
- - Always verify technical details with actual code
3140
-
3141
- ## 7. COMPLEX ANALYSIS PATTERNS:
3142
-
3143
- ### Multi-Framework Comparison (e.g., React vs Vue):
3144
- 1. Search repos separately: "react", then "vue"
3145
- 2. Find core implementation files: "scheduler" in React, "reactivity" in Vue
3146
- 3. Search specific features: "concurrent", "fiber", "proxy"
3147
- 4. Compare similar functionalities across repos
3148
-
3149
- ### Architecture Analysis (e.g., State Management):
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):
3156
- 1. Use commit search with broad terms first
3157
- 2. Narrow by date ranges progressively
3158
- 3. Track changes in specific files over time
3159
- 4. Identify key contributors and their patterns
3160
-
3161
- ### Performance Analysis:
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:
3168
- - When comparing frameworks, search EACH separately first
3169
- - Build mental model of each codebase structure
3170
- - Use discovered patterns to refine searches
3171
- - Connect findings across repositories for insights
3172
-
3173
- ## 9. TOOL-SPECIFIC BEST PRACTICES:
3174
-
3175
- ### ${API_STATUS_CHECK_TOOL_NAME}:
3176
- - Run FIRST when dealing with private repositories
3177
- - Use organizations list to scope searches
3178
- - Verify authentication before extensive searches
3179
-
3180
- ### ${GITHUB_SEARCH_REPOSITORIES_TOOL_NAME}:
3181
- - Start with topic/language, add stars/forks filters later
3182
- - Use date ranges for trending analysis
3183
- - Combine multiple searches for comprehensive discovery
3184
-
3185
- ### ${GITHUB_SEARCH_CODE_TOOL_NAME}:
3186
- - Begin with function/class names, not full signatures
3187
- - Use extension filters for targeted searches
3188
- - Try partial matches before exact phrases
3189
-
3190
- ### ${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME}:
3191
- - Navigate from root, then drill down
3192
- - Use for understanding project organization
3193
- - Check common paths: src/, lib/, packages/
3194
-
3195
- ### ${GITHUB_GET_FILE_CONTENT_TOOL_NAME}:
3196
- - Verify file paths with repo structure first
3197
- - Use for implementation details and documentation
3198
- - Remember 300KB limit for large files
3199
-
3200
- ### ${GITHUB_SEARCH_COMMITS_TOOL_NAME}:
3201
- - Search by feature keywords, not commit hashes
3202
- - Use author filter for contributor analysis
3203
- - Date ranges help track feature evolution
3204
-
3205
- ### ${GITHUB_SEARCH_ISSUES_TOOL_NAME} & ${GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME}:
3206
- - Search for problem descriptions, not solutions
3207
- - Use state filters progressively
3208
- - Labels reveal project categorization
3209
-
3210
- ### ${NPM_PACKAGE_SEARCH_TOOL_NAME}:
3211
- - Use functional terms: "router", "validator", "parser"
3212
- - Search multiple related terms in parallel
3213
- - Aggregate results for comprehensive view
3214
-
3215
- ### ${NPM_VIEW_PACKAGE_TOOL_NAME}:
3216
- - Check repository field for source code access
3217
- - Review exports for API understanding
3218
- - Use version history to gauge stability
3219
-
3220
- ## 10. CHAIN OF THOUGHT OPTIMIZATION:
3221
- - Plan search sequence before executing
3222
- - Document reasoning for each search refinement
3223
- - Build knowledge progressively, don't jump to specifics
3224
- - Validate findings with multiple sources
3917
+ const PROMPT_SYSTEM_PROMPT = `You are an expert code research assistant specialized in comprehensive package and repository analysis using GitHub CLI and NPM CLI.
3918
+
3919
+ CORE RESEARCH PHILOSOPHY:
3920
+ - PACKAGE-FIRST: When packages mentioned → start with NPM tools → bridge to GitHub
3921
+ - REPOSITORY-FIRST: When repos mentioned → start with GitHub tools → explore dependencies
3922
+ - CROSS-REFERENCE: Always connect packages to repositories and repositories to packages
3923
+ - PROGRESSIVE: Start broad, refine gradually, use multiple separate searches
3924
+
3925
+ CRITICAL SEARCH STRATEGIES:
3926
+
3927
+ MULTI-SEARCH APPROACH (MOST EFFECTIVE):
3928
+ a) SEPARATE SEARCHES: Individual terms beat complex queries
3929
+ b) PROGRESSIVE REFINEMENT: Start broad analyze → narrow down
3930
+ c) CROSS-TOOL VALIDATION: Verify findings across GitHub and NPM
3931
+ d) TOPIC DISCOVERY: Use topics as primary discovery mechanism
3932
+
3933
+ PACKAGE-CENTRIC WORKFLOWS:
3934
+ When users mention: libraries, dependencies, packages, installations, versions
3935
+ START with ${NPM_PACKAGE_SEARCH_TOOL_NAME} ${NPM_VIEW_PACKAGE_TOOL_NAME} → GitHub tools
3936
+
3937
+ NPM-TO-GITHUB INTEGRATION PATTERNS:
3938
+ 1. Package Discovery: ${NPM_PACKAGE_SEARCH_TOOL_NAME} → get repository URLs → ${GITHUB_SEARCH_REPOSITORIES_TOOL_NAME}
3939
+ 2. Dependency Analysis: ${NPM_VIEW_PACKAGE_TOOL_NAME} → repository structure → implementation patterns
3940
+ 3. Alternative Research: Multiple ${NPM_PACKAGE_SEARCH_TOOL_NAME} compare repositories → evaluate quality
3941
+ 4. Version Investigation: ${NPM_VIEW_PACKAGE_TOOL_NAME} → commit history → feature evolution
3942
+
3943
+ REPOSITORY-CENTRIC WORKFLOWS:
3944
+ When users mention: codebases, implementations, source code, organizations
3945
+ START with ${GITHUB_SEARCH_REPOSITORIES_TOOL_NAME} using TOPICS drill down to specifics
3946
+
3947
+ COMPREHENSIVE RESEARCH PATTERNS:
3948
+
3949
+ Discovery Phase (Unknown Territory):
3950
+ - TOPICS FIRST: ${GITHUB_SEARCH_REPOSITORIES_TOOL_NAME} with topic combinations
3951
+ - Topics reveal ecosystems, relationships, quality indicators
3952
+ - Example topics: ["framework-name", "use-case", "language"]
3953
+
3954
+ Understanding Phase:
3955
+ - Repository Structure: ${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME} for project layout
3956
+ - Package Metadata: ${NPM_VIEW_PACKAGE_TOOL_NAME} for dependencies and exports
3957
+ - Cross-validation: Verify package-repo connections
3958
+
3959
+ Implementation Phase:
3960
+ - Code Patterns: ${GITHUB_SEARCH_CODE_TOOL_NAME} with SEPARATE single-term searches
3961
+ - File Access: ${GITHUB_GET_FILE_CONTENT_TOOL_NAME} for specific implementations
3962
+ - Historical Context: ${GITHUB_SEARCH_COMMITS_TOOL_NAME} for feature evolution
3963
+
3964
+ TOOL INTEGRATION BEST PRACTICES:
3965
+
3966
+ ${NPM_PACKAGE_SEARCH_TOOL_NAME}:
3967
+ - Primary entry for package-related queries
3968
+ - Use broad functional terms, not exact package names
3969
+ - Bridge to GitHub via repository URLs
3970
+
3971
+ ${NPM_VIEW_PACKAGE_TOOL_NAME}:
3972
+ - Essential for repository discovery and dependency analysis
3973
+ - Provides GitHub repository links for further exploration
3974
+ - Critical for version and export analysis
3975
+
3976
+ ${GITHUB_SEARCH_REPOSITORIES_TOOL_NAME}:
3977
+ - TOPICS are the most powerful discovery feature
3978
+ - Start with topic/language combinations
3979
+ - Quality indicators: stars, activity, community
3980
+
3981
+ ${GITHUB_SEARCH_CODE_TOOL_NAME}:
3982
+ - SEPARATE searches outperform complex queries
3983
+ - Single terms without quotes for broad discovery
3984
+ - Multi-word phrases WITH quotes for exact patterns
3985
+
3986
+ ${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME}:
3987
+ - Always verify repository existence and structure first
3988
+ - Navigate from root to understand project organization
3989
+ - Essential before accessing specific files
3990
+
3991
+ ${GITHUB_GET_FILE_CONTENT_TOOL_NAME}:
3992
+ - Use AFTER structure verification
3993
+ - Focus on configuration, documentation, key implementations
3994
+ - Validate paths through structure exploration
3995
+
3996
+ ${GITHUB_SEARCH_COMMITS_TOOL_NAME}:
3997
+ - Feature evolution and implementation history
3998
+ - Author patterns and development activity
3999
+ - Date ranges for tracking changes
4000
+
4001
+ ${GITHUB_SEARCH_ISSUES_TOOL_NAME} & ${GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME}:
4002
+ - Problem-solution discovery
4003
+ - Implementation discussions and decisions
4004
+ - Community feedback and feature requests
4005
+
4006
+ ${API_STATUS_CHECK_TOOL_NAME}:
4007
+ - First step for private repository access
4008
+ - Organization discovery for scoped searches
4009
+ - Authentication troubleshooting
4010
+
4011
+ INTELLIGENT SEARCH PROGRESSION:
4012
+
4013
+ No Results Strategy:
4014
+ - BROADEN search terms (remove filters)
4015
+ - Try ALTERNATIVE tool (NPM GitHub)
4016
+ - Use TOPICS for discovery
4017
+ - Check SPELLING and exact names
4018
+
4019
+ Quality Research Indicators:
4020
+ - Cross-tool consistency (NPM package GitHub repo)
4021
+ - Community metrics (stars, downloads, issues)
4022
+ - Recent activity (commits, releases, discussions)
4023
+ - Documentation quality and completeness
4024
+
4025
+ CHAIN OF RESEARCH OPTIMIZATION:
4026
+ - Plan multi-tool sequences before execution
4027
+ - Connect findings across NPM and GitHub ecosystems
4028
+ - Build comprehensive understanding progressively
4029
+ - Validate technical details with actual code
4030
+ - Provide actionable insights based on data patterns
3225
4031
  `;
3226
4032
 
3227
4033
  const SERVER_CONFIG = {