octocode-mcp 2.3.6 → 2.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/build/index.js +789 -522
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -107,9 +107,9 @@ function escapeShellArg(arg, shellType, isGitHubQuery // Flag to indicate if thi
107
107
  const isWindows = platform() === 'win32';
108
108
  shellType = isWindows ? 'cmd' : 'unix';
109
109
  }
110
- // Special handling for GitHub search queries
110
+ // Special handling for GitHub search queries to preserve AND logic
111
111
  if (isGitHubQuery) {
112
- // If the argument already contains quotes, preserve them
112
+ // If the argument already contains quotes, preserve them for exact phrases
113
113
  if (arg.includes('"')) {
114
114
  // For Unix-like shells, wrap the entire argument in single quotes
115
115
  if (shellType === 'unix') {
@@ -122,6 +122,13 @@ function escapeShellArg(arg, shellType, isGitHubQuery // Flag to indicate if thi
122
122
  // For PowerShell
123
123
  return `'${arg.replace(/'/g, "''")}'`;
124
124
  }
125
+ // For space-separated terms (AND search), minimize escaping
126
+ if (arg.includes(' ') && shellType === 'unix') {
127
+ // Only escape if contains dangerous shell characters
128
+ if (!/[;&|<>$`\\]/.test(arg)) {
129
+ return `"${arg}"`;
130
+ }
131
+ }
125
132
  }
126
133
  switch (shellType) {
127
134
  case 'powershell':
@@ -158,16 +165,28 @@ function escapeWindowsCmdArg(arg) {
158
165
  }
159
166
  /**
160
167
  * Escape arguments for Unix shells with special handling for GitHub CLI queries
168
+ * Preserves AND search logic by not over-escaping space-separated terms
161
169
  */
162
170
  function escapeUnixShellArg(arg, isGitHubQuery) {
163
- // If it's a GitHub search query with special characters or spaces
164
- if (isGitHubQuery && (arg.includes(' ') || /[:"']/g.test(arg))) {
165
- // Preserve existing quotes if present
171
+ // For GitHub search queries, we need to preserve AND logic
172
+ if (isGitHubQuery) {
173
+ // If the query contains quotes, preserve them for exact phrase matching
166
174
  if (arg.includes('"')) {
175
+ // Use single quotes to wrap the entire query while preserving internal quotes
176
+ return `'${arg.replace(/'/g, "'\"'\"'")}'`;
177
+ }
178
+ // For space-separated terms (AND search), only escape if absolutely necessary
179
+ // GitHub CLI expects space-separated terms for AND logic
180
+ if (arg.includes(' ') && !/[;&|<>$`\\]/.test(arg)) {
181
+ // Only wrap in quotes if it contains shell metacharacters beyond spaces
182
+ return `"${arg}"`;
183
+ }
184
+ // For single terms or terms with special chars, escape normally
185
+ if (/[;&|<>$`\\]/.test(arg)) {
167
186
  return `'${arg.replace(/'/g, "'\"'\"'")}'`;
168
187
  }
169
- // Add double quotes for terms that need them
170
- return `"${arg}"`;
188
+ // Simple terms don't need escaping
189
+ return arg;
171
190
  }
172
191
  // Standard Unix shell escaping for other arguments
173
192
  if (/[^a-zA-Z0-9\-_./=@:]/.test(arg)) {
@@ -219,10 +238,15 @@ async function executeGitHubCommand(command, args = [], options = {}) {
219
238
  index > 1 &&
220
239
  (arg.includes(':') || arg.startsWith('(')) &&
221
240
  !arg.startsWith('--');
222
- // Don't escape GitHub search qualifiers - they need to be passed as-is
223
- // This includes qualifiers like "language:typescript", "user:microsoft", "org:microsoft"
224
- // and complex expressions like "(user:microsoft OR org:microsoft)"
241
+ // GitHub search qualifiers need special handling
242
+ // Most qualifiers can be passed as-is, but those with shell metacharacters need escaping
225
243
  if (isGitHubQualifier) {
244
+ // Check if the qualifier contains shell metacharacters that need escaping
245
+ if (/[<>&|;`$\\]/.test(arg)) {
246
+ // Escape qualifiers that contain shell metacharacters like size:<1000, size:>500
247
+ return escapeShellArg(arg, shellConfig.type, false);
248
+ }
249
+ // Safe qualifiers like "language:typescript", "user:microsoft" can be passed as-is
226
250
  return arg;
227
251
  }
228
252
  return escapeShellArg(arg, shellConfig.type, isMainQueryArgument);
@@ -284,6 +308,14 @@ async function executeCommand(fullCommand, type, options = {}, shellConfig) {
284
308
  stderr.trim() !== '';
285
309
  if (shouldTreatAsError) {
286
310
  const errorType = type === 'npm' ? 'NPM command error' : 'GitHub CLI command error';
311
+ // Enhanced error messaging for common GitHub issues
312
+ if (type === 'github' && stderr.includes('404')) {
313
+ const isRepoNotFound = stderr.includes('Not Found');
314
+ const enhancedMessage = isRepoNotFound
315
+ ? `${stderr}\n\nThis is often due to incorrect repository name. Use github_search_code to find the correct repository.`
316
+ : stderr;
317
+ return createErrorResult(errorType, new Error(enhancedMessage));
318
+ }
287
319
  return createErrorResult(errorType, new Error(stderr));
288
320
  }
289
321
  // Try to parse stdout as JSON, fallback to string if not possible
@@ -313,61 +345,6 @@ async function executeCommand(fullCommand, type, options = {}, shellConfig) {
313
345
  }
314
346
  }
315
347
 
316
- var LogLevel;
317
- (function (LogLevel) {
318
- LogLevel["ERROR"] = "ERROR";
319
- LogLevel["WARN"] = "WARN";
320
- LogLevel["INFO"] = "INFO";
321
- LogLevel["DEBUG"] = "DEBUG";
322
- })(LogLevel || (LogLevel = {}));
323
- class Logger {
324
- appName;
325
- timestamp;
326
- constructor(appName = 'octocode-mcp', timestamp = true) {
327
- this.appName = appName;
328
- this.timestamp = timestamp;
329
- }
330
- formatMessage(level, message, ...args) {
331
- const timestamp = this.timestamp ? `[${new Date().toISOString()}] ` : '';
332
- const prefix = `${timestamp}[${this.appName}] [${level}]`;
333
- const formattedArgs = args.length > 0
334
- ? ' ' +
335
- args
336
- .map(arg => {
337
- if (arg instanceof Error) {
338
- return `${arg.message}\n${arg.stack}`;
339
- }
340
- return typeof arg === 'object'
341
- ? JSON.stringify(arg, null, 2)
342
- : String(arg);
343
- })
344
- .join(' ')
345
- : '';
346
- return `${prefix} ${message}${formattedArgs}`;
347
- }
348
- error(message, ...args) {
349
- // eslint-disable-next-line no-console
350
- console.error(this.formatMessage(LogLevel.ERROR, message, ...args));
351
- }
352
- warn(message, ...args) {
353
- // eslint-disable-next-line no-console
354
- console.warn(this.formatMessage(LogLevel.WARN, message, ...args));
355
- }
356
- info(message, ...args) {
357
- // eslint-disable-next-line no-console
358
- console.log(this.formatMessage(LogLevel.INFO, message, ...args));
359
- }
360
- debug(message, ...args) {
361
- if (process.env.DEBUG === 'true' ||
362
- process.env.NODE_ENV === 'development') {
363
- // eslint-disable-next-line no-console
364
- console.log(this.formatMessage(LogLevel.DEBUG, message, ...args));
365
- }
366
- }
367
- }
368
- // Singleton instance
369
- const logger = new Logger();
370
-
371
348
  function createResult(options) {
372
349
  const { data, error } = options;
373
350
  if (error) {
@@ -387,7 +364,6 @@ function createResult(options) {
387
364
  };
388
365
  }
389
366
  catch (jsonError) {
390
- logger.error('JSON serialization failed:', jsonError);
391
367
  return {
392
368
  content: [
393
369
  {
@@ -582,7 +558,7 @@ function createSearchFailedError(type = 'code') {
582
558
  }
583
559
 
584
560
  const API_STATUS_CHECK_TOOL_NAME = 'apiStatusCheck';
585
- const DESCRIPTION$9 = `Check GitHub and NPM authentication status. Returns connected status and GitHub organizations for accessing private repositories. No parameters required.`;
561
+ const DESCRIPTION$9 = `Check GitHub and NPM authentication status and available organizations. Verifies API connections and returns user organizations for accessing private repositories. Essential for troubleshooting access issues. No parameters required.`;
586
562
  // Helper function to parse execution results with proper typing
587
563
  function parseExecResult(result) {
588
564
  if (!result.isError && result.content?.[0]?.text) {
@@ -714,235 +690,34 @@ function registerApiStatusCheckTool(server) {
714
690
  });
715
691
  }
716
692
 
717
- const GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME = 'githubViewRepoStructure';
718
- 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).`;
719
- function registerViewRepositoryStructureTool(server) {
720
- server.registerTool(GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME, {
721
- description: DESCRIPTION$8,
722
- inputSchema: {
723
- owner: z
724
- .string()
725
- .min(1)
726
- .max(100)
727
- .regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/, 'Invalid GitHub username/org format')
728
- .describe(`Repository owner/org name (e.g., 'microsoft', 'google', NOT 'microsoft/vscode')`),
729
- repo: z
730
- .string()
731
- .min(1)
732
- .max(100)
733
- .regex(/^[a-zA-Z0-9._-]+$/, 'Invalid repository name format')
734
- .describe('Repository name (case-sensitive)'),
735
- branch: z
736
- .string()
737
- .min(1)
738
- .max(255)
739
- .regex(/^[^\s]+$/, 'Branch name cannot contain spaces')
740
- .describe('Branch name. Falls back to default branch if not found'),
741
- path: z
742
- .string()
743
- .optional()
744
- .default('')
745
- .refine(path => !path.includes('..'), 'Path traversal not allowed')
746
- .refine(path => path.length <= 500, 'Path too long')
747
- .describe('Directory path within repository. Leave empty for root.'),
748
- },
749
- annotations: {
750
- title: 'GitHub Repository Explorer',
751
- readOnlyHint: true,
752
- destructiveHint: false,
753
- idempotentHint: true,
754
- openWorldHint: true,
755
- },
756
- }, async (args) => {
757
- try {
758
- const result = await viewRepositoryStructure(args);
759
- return result;
760
- }
761
- catch (error) {
762
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
763
- return createResult({
764
- error: `Failed to explore repository. ${errorMessage}. Verify repository exists and is accessible`,
765
- });
766
- }
767
- });
768
- }
769
- /**
770
- * Views the structure of a GitHub repository at a specific path.
771
- * Optimized for code analysis workflows with smart defaults and clear errors.
772
- */
773
- async function viewRepositoryStructure(params) {
774
- const cacheKey = generateCacheKey('gh-repo-structure', params);
775
- return withCache(cacheKey, async () => {
776
- const { owner, repo, branch, path = '' } = params;
777
- try {
778
- // Clean up path
779
- const cleanPath = path.startsWith('/') ? path.substring(1) : path;
780
- // Try the requested branch first, then fallback to main/master
781
- const branchesToTry = await getSmartBranchFallback(owner, repo, branch);
782
- let items = [];
783
- let usedBranch = branch;
784
- let lastError = null;
785
- let attemptCount = 0;
786
- const maxAttempts = 3; // Prevent infinite loops
787
- for (const tryBranch of branchesToTry) {
788
- if (attemptCount >= maxAttempts)
789
- break;
790
- attemptCount++;
791
- try {
792
- const apiPath = `/repos/${owner}/${repo}/contents/${cleanPath}?ref=${tryBranch}`;
793
- const result = await executeGitHubCommand('api', [apiPath], {
794
- cache: false,
795
- });
796
- if (!result.isError) {
797
- const execResult = JSON.parse(result.content[0].text);
798
- const apiItems = execResult.result;
799
- items = Array.isArray(apiItems) ? apiItems : [apiItems];
800
- usedBranch = tryBranch;
801
- break;
802
- }
803
- else {
804
- lastError = new Error(result.content[0].text);
805
- }
806
- }
807
- catch (error) {
808
- lastError = error instanceof Error ? error : new Error(String(error));
809
- // Try next branch
810
- continue;
811
- }
812
- }
813
- if (items.length === 0) {
814
- // Use the most descriptive error message
815
- const errorMsg = lastError?.message || 'Unknown error';
816
- if (errorMsg.includes('404') || errorMsg.includes('Not Found')) {
817
- if (path) {
818
- return createResult({
819
- error: `Path "${path}" not found. Verify the path or use github_search_code to find files`,
820
- });
821
- }
822
- else {
823
- return createResult({
824
- error: `Repository not found: ${owner}/${repo}. Check spelling and case sensitivity`,
825
- });
826
- }
827
- }
828
- else if (errorMsg.includes('403') || errorMsg.includes('Forbidden')) {
829
- return createResult({
830
- error: `Access denied to ${owner}/${repo}. Repository may be private - use api_status_check`,
831
- });
832
- }
833
- else {
834
- return createResult({
835
- error: `Failed to access ${owner}/${repo}. Check network connection`,
836
- });
837
- }
838
- }
839
- // Limit total items to 100 for efficiency
840
- const limitedItems = items.slice(0, 100);
841
- // Sort: directories first, then alphabetically
842
- limitedItems.sort((a, b) => {
843
- if (a.type !== b.type) {
844
- return a.type === 'dir' ? -1 : 1;
845
- }
846
- return a.name.localeCompare(b.name);
847
- });
848
- // Create simplified, token-efficient structure
849
- const files = limitedItems
850
- .filter(item => item.type === 'file')
851
- .map(item => ({
852
- name: item.name,
853
- size: item.size,
854
- url: item.path, // Use path for fetching
855
- }));
856
- const folders = limitedItems
857
- .filter(item => item.type === 'dir')
858
- .map(item => ({
859
- name: item.name,
860
- url: item.path, // Use path for browsing
861
- }));
862
- return createResult({
863
- data: {
864
- repository: `${owner}/${repo}`,
865
- branch: usedBranch,
866
- path: cleanPath || '/',
867
- githubBasePath: `https://api.github.com/repos/${owner}/${repo}/contents/`,
868
- files: {
869
- count: files.length,
870
- files: files,
871
- },
872
- folders: {
873
- count: folders.length,
874
- folders: folders,
875
- },
876
- },
877
- });
878
- }
879
- catch (error) {
880
- return createResult({
881
- error: `Failed to access repository. ${error}. Verify repository name and authentication`,
882
- });
883
- }
884
- });
885
- }
886
- /**
887
- * Smart branch detection with automatic fallback to common branch names.
888
- */
889
- async function getSmartBranchFallback(owner, repo, requestedBranch) {
890
- const branches = [requestedBranch];
891
- try {
892
- // Try to get repository info to find default branch
893
- const repoInfoResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
894
- cache: false,
895
- });
896
- if (!repoInfoResult.isError) {
897
- const execResult = JSON.parse(repoInfoResult.content[0].text);
898
- const repoData = execResult.result;
899
- const defaultBranch = repoData.default_branch;
900
- if (defaultBranch && !branches.includes(defaultBranch)) {
901
- branches.push(defaultBranch);
902
- }
903
- }
904
- }
905
- catch {
906
- // If we can't get repo info, proceed with standard fallbacks
907
- }
908
- // Add common branch names if not already included
909
- const commonBranches = ['main', 'master', 'develop', 'dev'];
910
- commonBranches.forEach(branch => {
911
- if (!branches.includes(branch)) {
912
- branches.push(branch);
913
- }
914
- });
915
- return branches;
916
- }
917
-
918
693
  const GITHUB_GET_FILE_CONTENT_TOOL_NAME = 'githubGetFileContent';
919
- 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).`;
694
+ const DESCRIPTION$8 = `Fetch file content from GitHub repositories. Automatically handles branch fallback (main/master) and files up to 300KB. Returns decoded file content with metadata. Parameters: owner (required - GitHub username/org), repo (required - repository name), branch (required), filePath (required).`;
920
695
  function registerFetchGitHubFileContentTool(server) {
921
696
  server.registerTool(GITHUB_GET_FILE_CONTENT_TOOL_NAME, {
922
- description: DESCRIPTION$7,
697
+ description: DESCRIPTION$8,
923
698
  inputSchema: {
924
699
  owner: z
925
700
  .string()
926
701
  .min(1)
927
702
  .max(100)
928
703
  .regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/)
929
- .describe(`Repository owner/org name (e.g., 'microsoft', 'google', NOT 'microsoft/vscode')`),
704
+ .describe(`Repository owner/organization name (e.g., 'facebook', 'microsoft'). Do NOT include repository name here.`),
930
705
  repo: z
931
706
  .string()
932
707
  .min(1)
933
708
  .max(100)
934
709
  .regex(/^[a-zA-Z0-9._-]+$/)
935
- .describe(`Repository name only (e.g., 'vscode', 'react', NOT 'microsoft/vscode')`),
710
+ .describe(`Repository name only (e.g., 'react', 'vscode'). Do NOT include owner/org prefix.`),
936
711
  branch: z
937
712
  .string()
938
713
  .min(1)
939
714
  .max(255)
940
715
  .regex(/^[^\s]+$/)
941
- .describe(`Branch name. Falls back to main/master if not found`),
716
+ .describe(`Branch name (e.g., 'main', 'master'). Tool will automatically try 'main' and 'master' if specified branch is not found.`),
942
717
  filePath: z
943
718
  .string()
944
719
  .min(1)
945
- .describe(`Exact file path from repo root (e.g., src/index.js, README.md)`),
720
+ .describe(`File path from repository root (e.g., 'src/index.js', 'README.md', 'docs/api.md'). Do NOT start with slash.`),
946
721
  },
947
722
  annotations: {
948
723
  title: 'GitHub File Content - Direct Access',
@@ -969,18 +744,39 @@ async function fetchGitHubFileContent(params) {
969
744
  const { owner, repo, branch, filePath } = params;
970
745
  try {
971
746
  // Try to fetch file content directly
972
- const apiPath = `/repos/${owner}/${repo}/contents/${filePath}?ref=${branch}`;
747
+ const apiPath = `/repos/${owner}/${repo}/contents/${filePath}`;
973
748
  const result = await executeGitHubCommand('api', [apiPath], {
974
749
  cache: false,
975
750
  });
976
751
  if (result.isError) {
977
752
  const errorMsg = result.content[0].text;
753
+ // Check repository existence only after content fetch fails
754
+ const repoCheckResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
755
+ cache: false,
756
+ });
757
+ if (repoCheckResult.isError) {
758
+ const repoErrorMsg = repoCheckResult.content[0].text;
759
+ if (repoErrorMsg.includes('404')) {
760
+ return createResult({
761
+ error: `Repository "${owner}/${repo}" not found. This is often due to incorrect repository name. Steps to resolve:\n1. Use github_search_code with query="${filePath.split('/').pop()}" owner="${owner}" to find the correct repository\n2. Verify the exact repository name\n3. Check if the repository might have been renamed or moved`,
762
+ });
763
+ }
764
+ else if (repoErrorMsg.includes('403')) {
765
+ return createResult({
766
+ error: `Repository "${owner}/${repo}" exists but access is denied. Repository might be private or archived. Use api_status_check to verify permissions.`,
767
+ });
768
+ }
769
+ }
978
770
  // Try fallback branches if original branch fails
979
771
  if (errorMsg.includes('404') &&
980
772
  branch !== 'main' &&
981
773
  branch !== 'master') {
982
774
  const fallbackBranches = ['main', 'master'];
775
+ const triedBranches = [branch];
983
776
  for (const fallbackBranch of fallbackBranches) {
777
+ if (triedBranches.includes(fallbackBranch))
778
+ continue;
779
+ triedBranches.push(fallbackBranch);
984
780
  const fallbackPath = `/repos/${owner}/${repo}/contents/${filePath}?ref=${fallbackBranch}`;
985
781
  const fallbackResult = await executeGitHubCommand('api', [fallbackPath], {
986
782
  cache: false,
@@ -989,27 +785,32 @@ async function fetchGitHubFileContent(params) {
989
785
  return await processFileContent(fallbackResult, owner, repo, fallbackBranch, filePath);
990
786
  }
991
787
  }
788
+ return createResult({
789
+ 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.`,
790
+ });
992
791
  }
993
- // Handle common errors
792
+ // Handle common errors with more context
994
793
  if (errorMsg.includes('404')) {
794
+ const searchSuggestion = await suggestCodeSearchFallback(owner, filePath);
995
795
  return createResult({
996
- error: 'File not found. Use github_view_repo_structure to explore repository structure',
796
+ error: `File "${filePath}" not found in branch "${branch}". Use github_view_repo_structure to verify path.${searchSuggestion}`,
997
797
  });
998
798
  }
999
799
  else if (errorMsg.includes('403')) {
1000
800
  return createResult({
1001
- error: 'Access denied. Repository may be private - use apiStatusCheck to verify',
801
+ 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.`,
1002
802
  });
1003
803
  }
1004
804
  else if (errorMsg.includes('maxBuffer') ||
1005
805
  errorMsg.includes('stdout maxBuffer length exceeded')) {
1006
806
  return createResult({
1007
- error: 'File too large (>300KB). Use githubSearchCode to search for patterns within the file',
807
+ 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.`,
1008
808
  });
1009
809
  }
1010
810
  else {
811
+ const searchSuggestion = await suggestCodeSearchFallback(owner, filePath);
1011
812
  return createResult({
1012
- error: 'Failed to fetch file. Verify repository name and file path',
813
+ error: `Failed to fetch "${filePath}". Error: ${errorMsg}. Verify repository name, branch, and file path.${searchSuggestion}`,
1013
814
  });
1014
815
  }
1015
816
  }
@@ -1017,15 +818,14 @@ async function fetchGitHubFileContent(params) {
1017
818
  }
1018
819
  catch (error) {
1019
820
  const errorMessage = error.message;
1020
- // Handle maxBuffer errors that escape the main try-catch
1021
821
  if (errorMessage.includes('maxBuffer') ||
1022
822
  errorMessage.includes('stdout maxBuffer length exceeded')) {
1023
823
  return createResult({
1024
- error: 'File too large (>300KB). Use github_search_code to search for patterns within the file',
824
+ error: `File "${filePath}" is too large (>300KB). Use github_search_code to search within the file or download directly from GitHub.`,
1025
825
  });
1026
826
  }
1027
827
  return createResult({
1028
- error: 'Unexpected error. Check network connection and try again',
828
+ error: `Unexpected error fetching "${filePath}": ${errorMessage}. Check network connection and try again.`,
1029
829
  });
1030
830
  }
1031
831
  });
@@ -1088,41 +888,82 @@ async function processFileContent(result, owner, repo, branch, filePath) {
1088
888
  },
1089
889
  });
1090
890
  }
891
+ // Helper function to suggest code search strategy
892
+ async function suggestCodeSearchFallback(owner, filePath) {
893
+ try {
894
+ // Extract filename and try to find in same organization
895
+ const fileName = filePath.split('/').pop() || filePath;
896
+ const searchResult = await executeGitHubCommand('api', [`/search/code?q=${encodeURIComponent(fileName)}+in:path+org:${owner}`], { cache: false });
897
+ if (!searchResult.isError) {
898
+ const results = JSON.parse(searchResult.content[0].text);
899
+ if (results.total_count > 0) {
900
+ const firstMatch = results.items[0];
901
+ return ` File might be in ${firstMatch.repository.full_name}. Try these searches:\n1. github_search_code with query="${fileName}" owner="${owner}"\n2. github_search_code with query="path:${filePath}" owner="${owner}"`;
902
+ }
903
+ }
904
+ }
905
+ catch {
906
+ // Fallback to generic message if search fails
907
+ }
908
+ return ` Try these searches:\n1. github_search_code with query="${filePath.split('/').pop()}" owner="${owner}"\n2. github_search_code with query="path:${filePath}"`;
909
+ }
1091
910
 
1092
911
  const GITHUB_SEARCH_CODE_TOOL_NAME = 'githubSearchCode';
1093
- const DESCRIPTION$6 = `Search code across GitHub repositories. Start with simple 1-2 word queries, then refine with filters like language, owner, or filename. Supports exact phrase matching with quotes. Parameters: query (required), language (optional), owner (optional - GitHub username/org, NOT owner/repo), filename (optional), extension (optional), match (optional), size (optional), limit (optional).`;
912
+ const DESCRIPTION$7 = `Search code across GitHub repositories using GitHub's code search API.
913
+
914
+ Never use filters and flags on exploretory searches and on initial searches.
915
+ Use filters and flags on subsequent searches when you know what to search for or if the user asks for it explicitly.
916
+ Using too many flags might make miss relevant results. Use filters and flags to narrow down the results when getting too many.
917
+
918
+ Search Syntax - ALL terms must be present (AND logic):
919
+ The search finds files that contain ALL specified terms. Each space-separated term is required to be present in the same file.
920
+ - Multiple terms: All individual words must exist in the file
921
+ - Quoted phrases: Exact phrase matching for multi-word expressions
922
+ - Mixed terms: Combination of individual words AND exact phrases, all must be present
923
+ - Additional filters: Language, owner, repository, filename, extension, size and other flags supported
924
+
925
+ Key behavior: All search terms are combined with AND logic - every term must be found in the file.
926
+ Quotes create exact phrase matching, unquoted terms are individual word requirements.
927
+ Start with 1-2 terms, add more to narrow results. Use filters and flags for precision.
928
+
929
+ `;
1094
930
  function registerGitHubSearchCodeTool(server) {
1095
931
  server.registerTool(GITHUB_SEARCH_CODE_TOOL_NAME, {
1096
- description: DESCRIPTION$6,
932
+ description: DESCRIPTION$7,
1097
933
  inputSchema: {
1098
934
  query: z
1099
935
  .string()
1100
936
  .min(1)
1101
- .describe('Search terms. START SIMPLE: Use 1-2 words first (e.g., "useState", "auth"). Add more terms only after seeing initial results.'),
937
+ .describe('Search query with AND logic between terms. Multiple words require ALL to be present. Use quotes for exact phrases.'),
1102
938
  language: z
1103
939
  .string()
1104
940
  .optional()
1105
- .describe('Language filter (javascript, python, etc). Use only when needed.'),
941
+ .describe('Programming language filter. Narrows search to specific language files. Use for language-specific searches.'),
1106
942
  owner: z
1107
943
  .union([z.string(), z.array(z.string())])
1108
944
  .optional()
1109
- .describe('Repository owner/org (for organization-specific searches). Format: username or org-name only, NOT owner/repo. Use this to search within a specific organization.'),
945
+ .describe('Repository owner/organization name(s) to search within (e.g., "facebook", ["google", "microsoft"]). Use this to search within specific organizations. Do NOT use owner/repo format - just the organization/username.'),
946
+ repo: z
947
+ .union([z.string(), z.array(z.string())])
948
+ .optional()
949
+ .describe('Filter on specific repository(ies). Must use full "owner/repo" format (e.g., "facebook/react", ["vuejs/vue", "angular/angular"]). Use this for searching within specific repositories.'),
1110
950
  filename: z
1111
951
  .string()
1112
952
  .optional()
1113
- .describe('Specific filename to search. Use for targeted searches.'),
953
+ .describe('Target specific filename or pattern. Use for file-specific searches.'),
1114
954
  extension: z
1115
955
  .string()
1116
956
  .optional()
1117
- .describe('File extension (.js, .py, etc). Alternative to language filter.'),
957
+ .describe('File extension filter. Alternative to language parameter.'),
1118
958
  match: z
1119
959
  .union([z.enum(['file', 'path']), z.array(z.enum(['file', 'path']))])
1120
960
  .optional()
1121
- .describe('Search scope: "file" for content, "path" for filenames. Default: file content.'),
961
+ .describe('Search scope: "file" for file content (default), "path" for filenames/paths, or ["file", "path"] for both. Controls where to search for terms.'),
1122
962
  size: z
1123
963
  .string()
964
+ .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid size format. Use: ">10", ">=5", "<100", "<=50", "10..100", or exact number "50"')
1124
965
  .optional()
1125
- .describe('File size in KB. Format: >10, <100, or 10..50'),
966
+ .describe('File size filter in KB. Format: ">N" (larger than), "<N" (smaller than), "N..M" (range), "N" (exact).'),
1126
967
  limit: z
1127
968
  .number()
1128
969
  .int()
@@ -1130,7 +971,7 @@ function registerGitHubSearchCodeTool(server) {
1130
971
  .max(100)
1131
972
  .optional()
1132
973
  .default(30)
1133
- .describe('Results limit (1-100). Default: 30'),
974
+ .describe('Maximum number of results to return (1-100). Default: 30. Higher values may increase response time.'),
1134
975
  },
1135
976
  annotations: {
1136
977
  title: 'GitHub Code Search - Smart & Efficient',
@@ -1236,6 +1077,10 @@ function transformToOptimizedFormat$1(items) {
1236
1077
  : [0, 0]) || [],
1237
1078
  })) || [],
1238
1079
  url: singleRepo ? item.path : simplifyGitHubUrl(item.url),
1080
+ repository: {
1081
+ nameWithOwner: item.repository.nameWithOwner,
1082
+ url: item.repository.url,
1083
+ },
1239
1084
  }));
1240
1085
  const result = {
1241
1086
  items: optimizedItems,
@@ -1262,32 +1107,18 @@ function extractSingleRepository$1(items) {
1262
1107
  }
1263
1108
  /**
1264
1109
  * Build command line arguments for GitHub CLI with improved parameter handling.
1265
- * Ensures exact string search capability with proper quote and escape handling.
1266
- *
1267
- * This function is refactored to correctly distinguish between search qualifiers
1268
- * (like `language` and `extension`), which will be passed as separate arguments
1269
- * to `gh search`, and command-line flags (like `--size` and `--limit`).
1110
+ * Preserves quoted phrases and supports both AND search and exact phrase matching.
1270
1111
  */
1271
1112
  function buildGitHubCliArgs(params) {
1272
1113
  const args = ['code'];
1273
- // Extract qualifiers from the query
1274
- const queryParts = params.query.trim().split(/\s+/);
1275
- const searchTerms = [];
1276
- const qualifiers = [];
1277
- queryParts.forEach(part => {
1278
- if (part.includes(':')) {
1279
- qualifiers.push(part);
1280
- }
1281
- else {
1282
- searchTerms.push(part);
1283
- }
1284
- });
1285
- // Add search terms if any
1286
- if (searchTerms.length > 0) {
1287
- args.push(searchTerms.join(' '));
1288
- }
1289
- // Add extracted qualifiers
1290
- qualifiers.forEach(qualifier => {
1114
+ // Parse query to preserve quoted phrases and extract qualifiers
1115
+ const { searchQuery, extractedQualifiers } = parseSearchQuery(params.query);
1116
+ // Add the main search query if present
1117
+ if (searchQuery) {
1118
+ args.push(searchQuery);
1119
+ }
1120
+ // Add extracted qualifiers from the query
1121
+ extractedQualifiers.forEach(qualifier => {
1291
1122
  args.push(qualifier);
1292
1123
  });
1293
1124
  // Add explicit parameters as qualifiers
@@ -1300,6 +1131,10 @@ function buildGitHubCliArgs(params) {
1300
1131
  const owners = Array.isArray(params.owner) ? params.owner : [params.owner];
1301
1132
  owners.forEach(owner => args.push(`org:${owner}`));
1302
1133
  }
1134
+ if (params.repo && !params.query.includes('repo:')) {
1135
+ const repos = Array.isArray(params.repo) ? params.repo : [params.repo];
1136
+ repos.forEach(repo => args.push(`repo:${repo}`));
1137
+ }
1303
1138
  if (params.filename && !params.query.includes('filename:')) {
1304
1139
  args.push(`filename:${params.filename}`);
1305
1140
  }
@@ -1382,12 +1217,48 @@ function validateSearchParameters(params) {
1382
1217
  // }
1383
1218
  return null; // No validation errors
1384
1219
  }
1220
+ /**
1221
+ * Parse search query to preserve quoted phrases and extract qualifiers.
1222
+ * Handles:
1223
+ * - Quoted phrases: "error handling" -> kept as single unit
1224
+ * - Multiple terms: react lifecycle -> both terms for AND search
1225
+ * - Mixed: "error handling" debug -> phrase + term
1226
+ * - Qualifiers: language:javascript -> extracted separately
1227
+ */
1228
+ function parseSearchQuery(query) {
1229
+ const qualifiers = [];
1230
+ const searchTerms = [];
1231
+ // Regular expression to match quoted strings or individual words/qualifiers
1232
+ const tokenRegex = /"([^"]+)"|([^\s]+)/g;
1233
+ let match;
1234
+ while ((match = tokenRegex.exec(query)) !== null) {
1235
+ const token = match[1] || match[2]; // match[1] is quoted content, match[2] is unquoted
1236
+ // Check if it's a qualifier (contains : but not inside quotes)
1237
+ if (!match[1] && token.includes(':') && /^[a-zA-Z]+:/.test(token)) {
1238
+ qualifiers.push(token);
1239
+ }
1240
+ else {
1241
+ // It's a search term (either quoted or unquoted)
1242
+ if (match[1]) {
1243
+ // Preserve quotes for exact phrase search
1244
+ searchTerms.push(`"${token}"`);
1245
+ }
1246
+ else {
1247
+ searchTerms.push(token);
1248
+ }
1249
+ }
1250
+ }
1251
+ return {
1252
+ searchQuery: searchTerms.join(' '),
1253
+ extractedQualifiers: qualifiers,
1254
+ };
1255
+ }
1385
1256
 
1386
1257
  const GITHUB_SEARCH_COMMITS_TOOL_NAME = 'githubSearchCommits';
1387
- 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).`;
1258
+ const DESCRIPTION$6 = `Search commit history across GitHub repositories. Find commits by message, author, date, or repository. Supports advanced filtering for comprehensive commit analysis. Returns commit SHA, message, author, and date information.`;
1388
1259
  function registerGitHubSearchCommitsTool(server) {
1389
1260
  server.registerTool(GITHUB_SEARCH_COMMITS_TOOL_NAME, {
1390
- description: DESCRIPTION$5,
1261
+ description: DESCRIPTION$6,
1391
1262
  inputSchema: {
1392
1263
  query: z
1393
1264
  .string()
@@ -1397,34 +1268,40 @@ function registerGitHubSearchCommitsTool(server) {
1397
1268
  owner: z
1398
1269
  .string()
1399
1270
  .optional()
1400
- .describe('Repository owner/org name only (e.g., "microsoft", "google", NOT "microsoft/vscode"). Use with repo parameter for repository-specific searches.'),
1271
+ .describe('Repository owner/organization name only (e.g., "facebook", "microsoft"). Do NOT include repository name. Must be used with repo parameter for repository-specific searches.'),
1401
1272
  repo: z
1402
1273
  .string()
1403
1274
  .optional()
1404
- .describe('Repository name only (e.g., "vscode", "react", NOT "owner/repo"). Must be used together with owner parameter.'),
1275
+ .describe('Repository name only (e.g., "react", "vscode"). Do NOT include owner prefix. Must be used together with owner parameter.'),
1405
1276
  // Author filters
1406
1277
  author: z
1407
1278
  .string()
1408
1279
  .optional()
1409
1280
  .describe('GitHub username of commit author'),
1410
- authorName: z
1281
+ 'author-name': z
1411
1282
  .string()
1412
1283
  .optional()
1413
1284
  .describe('Full name of commit author'),
1414
- authorEmail: z.string().optional().describe('Email of commit author'),
1285
+ 'author-email': z
1286
+ .string()
1287
+ .optional()
1288
+ .describe('Email of commit author'),
1415
1289
  // Committer filters
1416
1290
  committer: z
1417
1291
  .string()
1418
1292
  .optional()
1419
1293
  .describe('GitHub username of committer'),
1420
- committerName: z.string().optional().describe('Full name of committer'),
1421
- committerEmail: z.string().optional().describe('Email of committer'),
1294
+ 'committer-name': z
1295
+ .string()
1296
+ .optional()
1297
+ .describe('Full name of committer'),
1298
+ 'committer-email': z.string().optional().describe('Email of committer'),
1422
1299
  // Date filters
1423
- authorDate: z
1300
+ 'author-date': z
1424
1301
  .string()
1425
1302
  .optional()
1426
1303
  .describe('When authored. Format: >2020-01-01, <2023-12-31, 2020-01-01..2023-12-31'),
1427
- committerDate: z
1304
+ 'committer-date': z
1428
1305
  .string()
1429
1306
  .optional()
1430
1307
  .describe('When committed. Format: >2020-01-01, <2023-12-31, 2020-01-01..2023-12-31'),
@@ -1603,22 +1480,22 @@ function buildGitHubCommitCliArgs(params) {
1603
1480
  // Author filters
1604
1481
  if (params.author)
1605
1482
  args.push(`--author=${params.author}`);
1606
- if (params.authorName)
1607
- args.push(`--author-name=${params.authorName}`);
1608
- if (params.authorEmail)
1609
- args.push(`--author-email=${params.authorEmail}`);
1483
+ if (params['author-name'])
1484
+ args.push(`--author-name=${params['author-name']}`);
1485
+ if (params['author-email'])
1486
+ args.push(`--author-email=${params['author-email']}`);
1610
1487
  // Committer filters
1611
1488
  if (params.committer)
1612
1489
  args.push(`--committer=${params.committer}`);
1613
- if (params.committerName)
1614
- args.push(`--committer-name=${params.committerName}`);
1615
- if (params.committerEmail)
1616
- args.push(`--committer-email=${params.committerEmail}`);
1490
+ if (params['committer-name'])
1491
+ args.push(`--committer-name=${params['committer-name']}`);
1492
+ if (params['committer-email'])
1493
+ args.push(`--committer-email=${params['committer-email']}`);
1617
1494
  // Date filters
1618
- if (params.authorDate)
1619
- args.push(`--author-date=${params.authorDate}`);
1620
- if (params.committerDate)
1621
- args.push(`--committer-date=${params.committerDate}`);
1495
+ if (params['author-date'])
1496
+ args.push(`--author-date=${params['author-date']}`);
1497
+ if (params['committer-date'])
1498
+ args.push(`--committer-date=${params['committer-date']}`);
1622
1499
  // Hash filters
1623
1500
  if (params.hash)
1624
1501
  args.push(`--hash=${params.hash}`);
@@ -1628,7 +1505,7 @@ function buildGitHubCommitCliArgs(params) {
1628
1505
  args.push(`--tree=${params.tree}`);
1629
1506
  // State filters
1630
1507
  if (params.merge !== undefined)
1631
- args.push(`--merge=${params.merge}`);
1508
+ args.push(`--merge`);
1632
1509
  // Visibility
1633
1510
  if (params.visibility)
1634
1511
  args.push(`--visibility=${params.visibility}`);
@@ -1645,10 +1522,10 @@ function buildGitHubCommitCliArgs(params) {
1645
1522
  }
1646
1523
 
1647
1524
  const GITHUB_SEARCH_ISSUES_TOOL_NAME = 'githubSearchIssues';
1648
- 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).`;
1525
+ const DESCRIPTION$5 = `Search GitHub issues for bug reports, feature requests, and discussions. Find issues by keywords, state, labels, author, or repository. Returns issue number, title, state, labels, and metadata for effective issue tracking and analysis.`;
1649
1526
  function registerSearchGitHubIssuesTool(server) {
1650
1527
  server.registerTool(GITHUB_SEARCH_ISSUES_TOOL_NAME, {
1651
- description: DESCRIPTION$4,
1528
+ description: DESCRIPTION$5,
1652
1529
  inputSchema: {
1653
1530
  query: z
1654
1531
  .string()
@@ -1658,11 +1535,11 @@ function registerSearchGitHubIssuesTool(server) {
1658
1535
  .string()
1659
1536
  .min(1)
1660
1537
  .optional()
1661
- .describe('Repository owner/org name only (e.g., "microsoft", "google", NOT "microsoft/vscode"). Use with repo parameter for repository-specific searches.'),
1538
+ .describe('Repository owner/organization name only (e.g., "facebook", "microsoft"). Do NOT include repository name. Must be used with repo parameter for repository-specific searches.'),
1662
1539
  repo: z
1663
1540
  .string()
1664
1541
  .optional()
1665
- .describe('Repository name only (e.g., "vscode", "react", NOT "owner/repo"). Must be used together with owner parameter.'),
1542
+ .describe('Repository name only (e.g., "react", "vscode"). Do NOT include owner prefix. Must be used together with owner parameter.'),
1666
1543
  app: z
1667
1544
  .string()
1668
1545
  .optional()
@@ -1678,33 +1555,45 @@ function registerSearchGitHubIssuesTool(server) {
1678
1555
  .describe('GitHub username of issue creator'),
1679
1556
  closed: z
1680
1557
  .string()
1558
+ .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"')
1681
1559
  .optional()
1682
- .describe('When closed. Format: >2020-01-01'),
1560
+ .describe('When closed. Format: ">2020-01-01" (after), ">=2020-01-01" (on or after), "<2023-12-31" (before), "2020-01-01..2023-12-31" (range)'),
1683
1561
  commenter: z
1684
1562
  .string()
1685
1563
  .optional()
1686
1564
  .describe('User who commented on issue'),
1687
1565
  comments: z
1688
- .number()
1566
+ .union([
1567
+ z.number().int().min(0),
1568
+ z
1569
+ .string()
1570
+ .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">10", ">=5", "<20", "<=15", "5..20", or exact number "10"'),
1571
+ ])
1689
1572
  .optional()
1690
- .describe('Comment count. Format: >10, <5, 5..10'),
1573
+ .describe('Comment count filter. Format: ">10" (many comments), ">=5" (at least 5), "<5" (few comments), "5..10" (range), "10" (exact)'),
1691
1574
  created: z
1692
1575
  .string()
1576
+ .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"')
1693
1577
  .optional()
1694
- .describe('When created. Format: >2020-01-01'),
1695
- includePrs: z
1578
+ .describe('When created. Format: ">2020-01-01" (after), ">=2020-01-01" (on or after), "<2023-12-31" (before), "2020-01-01..2023-12-31" (range)'),
1579
+ 'include-prs': z
1696
1580
  .boolean()
1697
1581
  .optional()
1698
1582
  .describe('Include pull requests. Default: false'),
1699
1583
  interactions: z
1700
- .number()
1584
+ .union([
1585
+ z.number().int().min(0),
1586
+ z
1587
+ .string()
1588
+ .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">100", ">=50", "<200", "<=150", "50..200", or exact number "100"'),
1589
+ ])
1701
1590
  .optional()
1702
- .describe('Total interactions (reactions + comments)'),
1591
+ .describe('Total interactions (reactions + comments) filter. Format: ">100" (highly engaged), ">=50" (moderately engaged), "<20" (low engagement), "50..200" (range)'),
1703
1592
  involves: z.string().optional().describe('User involved in any way'),
1704
- labels: z
1705
- .string()
1593
+ label: z
1594
+ .union([z.string(), z.array(z.string())])
1706
1595
  .optional()
1707
- .describe('Label names (bug, feature, etc.)'),
1596
+ .describe('Label names. Can be single string or array.'),
1708
1597
  language: z.string().optional().describe('Repository language'),
1709
1598
  locked: z.boolean().optional().describe('Conversation locked status'),
1710
1599
  match: z
@@ -1713,27 +1602,39 @@ function registerSearchGitHubIssuesTool(server) {
1713
1602
  .describe('Search scope. Default: title and body'),
1714
1603
  mentions: z.string().optional().describe('Issues mentioning this user'),
1715
1604
  milestone: z.string().optional().describe('Milestone name'),
1716
- noAssignee: z.boolean().optional().describe('Issues without assignee'),
1717
- noLabel: z.boolean().optional().describe('Issues without labels'),
1718
- noMilestone: z
1605
+ 'no-assignee': z
1606
+ .boolean()
1607
+ .optional()
1608
+ .describe('Issues without assignee'),
1609
+ 'no-label': z.boolean().optional().describe('Issues without labels'),
1610
+ 'no-milestone': z
1719
1611
  .boolean()
1720
1612
  .optional()
1721
1613
  .describe('Issues without milestone'),
1722
- noProject: z.boolean().optional().describe('Issues not in projects'),
1614
+ 'no-project': z.boolean().optional().describe('Issues not in projects'),
1723
1615
  project: z.string().optional().describe('Project board number'),
1724
1616
  reactions: z
1725
- .number()
1617
+ .union([
1618
+ z.number().int().min(0),
1619
+ z
1620
+ .string()
1621
+ .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">10", ">=5", "<50", "<=25", "5..50", or exact number "10"'),
1622
+ ])
1726
1623
  .optional()
1727
- .describe('Reaction count. Format: >10'),
1624
+ .describe('Reaction count filter. Format: ">10" (popular), ">=5" (some reactions), "<50" (moderate), "5..50" (range), "10" (exact)'),
1728
1625
  state: z
1729
1626
  .enum(['open', 'closed'])
1730
1627
  .optional()
1731
1628
  .describe('Issue state. Default: all'),
1732
- teamMentions: z.string().optional().describe('Team mentioned in issue'),
1629
+ 'team-mentions': z
1630
+ .string()
1631
+ .optional()
1632
+ .describe('Team mentioned in issue (@org/team-name)'),
1733
1633
  updated: z
1734
1634
  .string()
1635
+ .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"')
1735
1636
  .optional()
1736
- .describe('When updated. Format: >2020-01-01'),
1637
+ .describe('When updated. Format: ">2020-01-01" (after), ">=2020-01-01" (on or after), "<2023-12-31" (before), "2020-01-01..2023-12-31" (range)'),
1737
1638
  visibility: z
1738
1639
  .enum(['public', 'private', 'internal'])
1739
1640
  .optional()
@@ -1889,23 +1790,40 @@ function buildGitHubIssuesAPICommand(params) {
1889
1790
  queryParts.push(`${key}:${value}`);
1890
1791
  });
1891
1792
  // Special qualifiers - handle labels carefully
1892
- if (params.labels) {
1893
- queryParts.push(`label:"${params.labels}"`);
1793
+ if (params.label) {
1794
+ const labels = Array.isArray(params.label) ? params.label : [params.label];
1795
+ labels.forEach(label => queryParts.push(`label:"${label}"`));
1894
1796
  }
1895
1797
  if (params.milestone)
1896
1798
  queryParts.push(`milestone:"${params.milestone}"`);
1897
- if (params.noAssignee)
1799
+ if (params['no-assignee'])
1898
1800
  queryParts.push('no:assignee');
1899
- if (params.noLabel)
1801
+ if (params['no-label'])
1900
1802
  queryParts.push('no:label');
1901
- if (params.noMilestone)
1803
+ if (params['no-milestone'])
1902
1804
  queryParts.push('no:milestone');
1805
+ if (params['no-project'])
1806
+ queryParts.push('no:project');
1903
1807
  if (params.archived !== undefined)
1904
1808
  queryParts.push(`archived:${params.archived}`);
1905
1809
  if (params.locked)
1906
1810
  queryParts.push('is:locked');
1907
1811
  if (params.visibility)
1908
1812
  queryParts.push(`is:${params.visibility}`);
1813
+ if (params['include-prs'])
1814
+ queryParts.push('is:pr');
1815
+ if (params['team-mentions'])
1816
+ queryParts.push(`team:${params['team-mentions']}`);
1817
+ if (params.project)
1818
+ queryParts.push(`project:${params.project}`);
1819
+ if (params.app)
1820
+ queryParts.push(`app:${params.app}`);
1821
+ if (params.comments)
1822
+ queryParts.push(`comments:${params.comments}`);
1823
+ if (params.interactions)
1824
+ queryParts.push(`interactions:${params.interactions}`);
1825
+ if (params.reactions)
1826
+ queryParts.push(`reactions:${params.reactions}`);
1909
1827
  // Extract qualifiers from original query and add them if not already set by params
1910
1828
  if (baseQuery.includes('is:') && !params.state) {
1911
1829
  const isMatch = baseQuery.match(/\bis:(open|closed)\b/i);
@@ -1913,7 +1831,7 @@ function buildGitHubIssuesAPICommand(params) {
1913
1831
  queryParts.push(`state:${isMatch[1].toLowerCase()}`);
1914
1832
  }
1915
1833
  }
1916
- if (baseQuery.includes('label:') && !params.labels) {
1834
+ if (baseQuery.includes('label:') && !params.label) {
1917
1835
  const labelMatch = baseQuery.match(/\blabel:("[^"]*"|[^\s]+)/i);
1918
1836
  if (labelMatch) {
1919
1837
  const labelValue = labelMatch[1].replace(/"/g, '');
@@ -1938,10 +1856,10 @@ function buildGitHubIssuesAPICommand(params) {
1938
1856
 
1939
1857
  // TODO: add PR commeents. e.g, gh pr view <PR_NUMBER_OR_URL_OR_BRANCH> --comments
1940
1858
  const GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME = 'githubSearchPullRequests';
1941
- 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).`;
1859
+ const DESCRIPTION$4 = `Search GitHub pull requests for code changes, feature implementations, and bug fixes. Find PRs by keywords, state, author, review status, or repository. Returns PR number, title, state, branches, and review information for code review analysis.`;
1942
1860
  function registerSearchGitHubPullRequestsTool(server) {
1943
1861
  server.registerTool(GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME, {
1944
- description: DESCRIPTION$3,
1862
+ description: DESCRIPTION$4,
1945
1863
  inputSchema: {
1946
1864
  query: z
1947
1865
  .string()
@@ -1950,18 +1868,21 @@ function registerSearchGitHubPullRequestsTool(server) {
1950
1868
  owner: z
1951
1869
  .string()
1952
1870
  .optional()
1953
- .describe('Repository owner/org name only (e.g., "microsoft", "google", NOT "microsoft/vscode"). Use with repo parameter for repository-specific searches.'),
1871
+ .describe('Repository owner/organization name only (e.g., "facebook", "microsoft"). Do NOT include repository name. Must be used with repo parameter for repository-specific searches.'),
1954
1872
  repo: z
1955
1873
  .string()
1956
1874
  .optional()
1957
- .describe('Repository name only (e.g., "vscode", "react", NOT "owner/repo"). Must be used together with owner parameter.'),
1875
+ .describe('Repository name only (e.g., "react", "vscode"). Do NOT include owner prefix. Must be used together with owner parameter.'),
1958
1876
  author: z.string().optional().describe('GitHub username of PR author'),
1959
1877
  assignee: z.string().optional().describe('GitHub username of assignee'),
1960
1878
  mentions: z.string().optional().describe('PRs mentioning this user'),
1961
1879
  commenter: z.string().optional().describe('User who commented on PR'),
1962
1880
  involves: z.string().optional().describe('User involved in any way'),
1963
- reviewedBy: z.string().optional().describe('User who reviewed the PR'),
1964
- reviewRequested: z
1881
+ 'reviewed-by': z
1882
+ .string()
1883
+ .optional()
1884
+ .describe('User who reviewed the PR'),
1885
+ 'review-requested': z
1965
1886
  .string()
1966
1887
  .optional()
1967
1888
  .describe('User/team requested for review'),
@@ -1970,10 +1891,7 @@ function registerSearchGitHubPullRequestsTool(server) {
1970
1891
  .optional()
1971
1892
  .describe('PR state. Default: all'),
1972
1893
  head: z.string().optional().describe('Source branch name'),
1973
- base: z
1974
- .string()
1975
- .optional()
1976
- .describe('Target branch name (main, develop, etc.)'),
1894
+ base: z.string().optional().describe('Target branch name'),
1977
1895
  language: z.string().optional().describe('Repository language'),
1978
1896
  created: z
1979
1897
  .string()
@@ -1983,7 +1901,7 @@ function registerSearchGitHubPullRequestsTool(server) {
1983
1901
  .string()
1984
1902
  .optional()
1985
1903
  .describe('When updated. Format: >2020-01-01'),
1986
- mergedAt: z
1904
+ 'merged-at': z
1987
1905
  .string()
1988
1906
  .optional()
1989
1907
  .describe('When merged. Format: >2020-01-01'),
@@ -2004,6 +1922,49 @@ function registerSearchGitHubPullRequestsTool(server) {
2004
1922
  .enum(['none', 'required', 'approved', 'changes_requested'])
2005
1923
  .optional()
2006
1924
  .describe('Review status filter'),
1925
+ app: z.string().optional().describe('GitHub App that created the PR'),
1926
+ archived: z
1927
+ .boolean()
1928
+ .optional()
1929
+ .describe('Include archived repositories'),
1930
+ comments: z
1931
+ .number()
1932
+ .optional()
1933
+ .describe('Comment count filter. Format: >10, <5, 5..10'),
1934
+ interactions: z
1935
+ .number()
1936
+ .optional()
1937
+ .describe('Total interactions (reactions + comments)'),
1938
+ 'team-mentions': z
1939
+ .string()
1940
+ .optional()
1941
+ .describe('Team mentioned in PR (@org/team-name)'),
1942
+ reactions: z
1943
+ .number()
1944
+ .optional()
1945
+ .describe('Reaction count filter. Format: >10'),
1946
+ locked: z.boolean().optional().describe('Conversation locked status'),
1947
+ 'no-assignee': z.boolean().optional().describe('PRs without assignee'),
1948
+ 'no-label': z.boolean().optional().describe('PRs without labels'),
1949
+ 'no-milestone': z
1950
+ .boolean()
1951
+ .optional()
1952
+ .describe('PRs without milestone'),
1953
+ 'no-project': z.boolean().optional().describe('PRs not in projects'),
1954
+ label: z
1955
+ .union([z.string(), z.array(z.string())])
1956
+ .optional()
1957
+ .describe('Label names. Can be single string or array.'),
1958
+ milestone: z.string().optional().describe('Milestone title'),
1959
+ project: z.string().optional().describe('Project board owner/number'),
1960
+ visibility: z
1961
+ .enum(['public', 'private', 'internal'])
1962
+ .optional()
1963
+ .describe('Repository visibility'),
1964
+ match: z
1965
+ .enum(['title', 'body', 'comments'])
1966
+ .optional()
1967
+ .describe('Search scope. Default: title and body'),
2007
1968
  limit: z
2008
1969
  .number()
2009
1970
  .int()
@@ -2138,16 +2099,16 @@ function buildGitHubPullRequestsAPICommand(params) {
2138
2099
  queryParts.push(`${key}:${value}`);
2139
2100
  });
2140
2101
  // Special qualifiers
2141
- if (params.reviewedBy)
2142
- queryParts.push(`reviewed-by:${params.reviewedBy}`);
2143
- if (params.reviewRequested)
2144
- queryParts.push(`review-requested:${params.reviewRequested}`);
2102
+ if (params['reviewed-by'])
2103
+ queryParts.push(`reviewed-by:${params['reviewed-by']}`);
2104
+ if (params['review-requested'])
2105
+ queryParts.push(`review-requested:${params['review-requested']}`);
2145
2106
  if (params.head)
2146
2107
  queryParts.push(`head:${params.head}`);
2147
2108
  if (params.base)
2148
2109
  queryParts.push(`base:${params.base}`);
2149
- if (params.mergedAt)
2150
- queryParts.push(`merged:${params.mergedAt}`);
2110
+ if (params['merged-at'])
2111
+ queryParts.push(`merged:${params['merged-at']}`);
2151
2112
  if (params.draft !== undefined)
2152
2113
  queryParts.push(`draft:${params.draft}`);
2153
2114
  if (params.checks)
@@ -2156,6 +2117,39 @@ function buildGitHubPullRequestsAPICommand(params) {
2156
2117
  queryParts.push(`is:${params.merged ? 'merged' : 'unmerged'}`);
2157
2118
  if (params.review)
2158
2119
  queryParts.push(`review:${params.review}`);
2120
+ // Additional parameters
2121
+ if (params.app)
2122
+ queryParts.push(`app:${params.app}`);
2123
+ if (params.archived !== undefined)
2124
+ queryParts.push(`archived:${params.archived}`);
2125
+ if (params.comments)
2126
+ queryParts.push(`comments:${params.comments}`);
2127
+ if (params.interactions)
2128
+ queryParts.push(`interactions:${params.interactions}`);
2129
+ if (params.reactions)
2130
+ queryParts.push(`reactions:${params.reactions}`);
2131
+ if (params.locked)
2132
+ queryParts.push('is:locked');
2133
+ if (params.visibility)
2134
+ queryParts.push(`is:${params.visibility}`);
2135
+ if (params['team-mentions'])
2136
+ queryParts.push(`team:${params['team-mentions']}`);
2137
+ if (params['no-assignee'])
2138
+ queryParts.push('no:assignee');
2139
+ if (params['no-label'])
2140
+ queryParts.push('no:label');
2141
+ if (params['no-milestone'])
2142
+ queryParts.push('no:milestone');
2143
+ if (params['no-project'])
2144
+ queryParts.push('no:project');
2145
+ if (params.label) {
2146
+ const labels = Array.isArray(params.label) ? params.label : [params.label];
2147
+ labels.forEach(label => queryParts.push(`label:"${label}"`));
2148
+ }
2149
+ if (params.milestone)
2150
+ queryParts.push(`milestone:"${params.milestone}"`);
2151
+ if (params.project)
2152
+ queryParts.push(`project:${params.project}`);
2159
2153
  // Add type qualifier to search only pull requests
2160
2154
  queryParts.push('type:pr');
2161
2155
  const query = queryParts.filter(Boolean).join(' ');
@@ -2185,11 +2179,27 @@ function buildGitHubPullRequestsAPICommand(params) {
2185
2179
  * 4. Recent Quality:
2186
2180
  * { stars: ">1000", created: ">2023-01-01", limit: 10 }
2187
2181
  *
2182
+ * RESEARCH & EXPLORATION PATTERNS:
2183
+ *
2184
+ * 1. Topic-based Discovery (HIGHLY RECOMMENDED for unknown projects):
2185
+ * { topic: ["machine-learning", "nlp", "pytorch"], limit: 20 }
2186
+ * { topic: ["kubernetes", "monitoring"], stars: ">100", limit: 15 }
2187
+ *
2188
+ * 2. Exploratory Research Flow:
2189
+ * - Start with topics to discover repositories
2190
+ * - Then use githubViewRepoStructure to understand project layout
2191
+ * - Read README.md, docs/, and configuration files
2192
+ * - Finally use githubSearchCode for specific implementations
2193
+ *
2188
2194
  * AVOID: OR queries + language filter, 5+ filters, multi-word OR
2189
2195
  * TIP: Use limit parameter instead of adding more filters
2190
2196
  */
2191
2197
  const GITHUB_SEARCH_REPOSITORIES_TOOL_NAME = 'githubSearchRepositories';
2192
- const DESCRIPTION$2 = `Discover GitHub repositories with smart filtering. Supports language, stars, topics, ownership, dates, and community metrics. Parameters: query (optional), owner (optional - GitHub username/org, NOT owner/repo), language (optional), stars (optional), topic (optional), forks (optional), numberOfTopics (optional), license (optional), archived (optional), includeForks (optional), visibility (optional), created (optional), updated (optional), size (optional), goodFirstIssues (optional), helpWantedIssues (optional), followers (optional), match (optional), sort (optional), order (optional), limit (optional).`;
2198
+ const DESCRIPTION$3 = `Search GitHub repositories by name, description, topics, language, or organization. Find projects based on stars, forks, activity, and community metrics. Returns repository details including name, description, stars, language, and owner information for project discovery.
2199
+
2200
+ Search Syntax - ALL terms must be present (AND logic):
2201
+ Multiple search terms require ALL to be found in the repository (name, description, or README).
2202
+ Use quotes for exact phrase matching. Additional filters supported via parameters.`;
2193
2203
  /**
2194
2204
  * Extract owner/repo information from various query formats
2195
2205
  */
@@ -2222,70 +2232,88 @@ function extractOwnerRepoFromQuery(query) {
2222
2232
  }
2223
2233
  function registerSearchGitHubReposTool(server) {
2224
2234
  server.registerTool(GITHUB_SEARCH_REPOSITORIES_TOOL_NAME, {
2225
- description: DESCRIPTION$2,
2235
+ description: DESCRIPTION$3,
2226
2236
  inputSchema: {
2227
2237
  query: z
2228
2238
  .string()
2229
2239
  .optional()
2230
- .describe('Search query. START SIMPLE: Use 1-2 words with NO filters first (e.g., "react", "auth"). Add qualifiers only after initial search.'),
2240
+ .describe('Search query with AND logic between terms. Multiple words require ALL to be present. Use quotes for exact phrases.'),
2231
2241
  // CORE FILTERS (GitHub CLI flags)
2232
2242
  owner: z
2233
2243
  .union([z.string(), z.array(z.string())])
2234
2244
  .optional()
2235
- .describe('Repository owner or organization name only (e.g., "microsoft", "google", NOT "microsoft/vscode"). For private repos, use organizations from api_status_check (user_organizations). Can be a single value or array.'),
2245
+ .describe('Repository owner/organization name(s) (e.g., "facebook", ["google", "microsoft"]). Search within specific organizations. Do NOT use owner/repo format - just the organization/username.'),
2236
2246
  language: z
2237
2247
  .string()
2238
2248
  .optional()
2239
- .describe('Programming language filter. Use when results need refinement.'),
2249
+ .describe('Programming language filter. Filters repositories by primary language. Essential for language-specific searches.'),
2240
2250
  stars: z
2241
2251
  .union([
2242
2252
  z.number().int().min(0),
2243
- z.string().regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/),
2253
+ z
2254
+ .string()
2255
+ .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">1000", ">=500", "<100", "<=50", "10..100", or exact number "50"'),
2244
2256
  ])
2245
2257
  .optional()
2246
- .describe('Stars filter. Supports ranges and thresholds.'),
2258
+ .describe('Star count filter. Format: ">1000" (more than), ">=500" (more than or equal), "<100" (less than), "<=50" (less than or equal), "100..1000" (range), "500" (exact).'),
2247
2259
  topic: z
2248
2260
  .union([z.string(), z.array(z.string())])
2249
2261
  .optional()
2250
- .describe('Topics filter. Can be a single value or array.'),
2251
- forks: z.number().optional().describe('Number of forks filter.'),
2252
- // UPDATED: Match CLI parameter name exactly
2253
- numberOfTopics: z
2254
- .number()
2262
+ .describe('Repository topics filter. Excellent for discovering projects and understanding repository ecosystems. Topics use kebab-case format.'),
2263
+ forks: z
2264
+ .union([
2265
+ z.number().int().min(0),
2266
+ z
2267
+ .string()
2268
+ .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">100", ">=50", "<10", "<=5", "10..100", or exact number "5"'),
2269
+ ])
2270
+ .optional()
2271
+ .describe('Fork count filter. Format: ">100" (more than), ">=50" (more than or equal), "<10" (less than), "<=5" (less than or equal), "10..100" (range), "5" (exact).'),
2272
+ // Match CLI parameter name exactly
2273
+ 'number-topics': z
2274
+ .union([
2275
+ z.number().int().min(0),
2276
+ z
2277
+ .string()
2278
+ .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">5", ">=3", "<10", "<=2", "3..10", or exact number "5"'),
2279
+ ])
2255
2280
  .optional()
2256
- .describe('Filter by number of topics (indicates documentation quality).'),
2281
+ .describe('Number of topics filter. Format: ">5" (many topics), ">=3" (at least 3), "<10" (few topics), "1..3" (range), "5" (exact).'),
2257
2282
  // QUALITY & STATE FILTERS
2258
2283
  license: z
2259
2284
  .union([z.string(), z.array(z.string())])
2260
2285
  .optional()
2261
- .describe('License filter. Works well as array ["mit", "apache-2.0"].'),
2286
+ .describe('License filter.'),
2262
2287
  archived: z
2263
2288
  .boolean()
2264
2289
  .optional()
2265
- .describe('Filter archived repositories (true/false).'),
2266
- includeForks: z
2290
+ .describe('Archive status filter. false (active repos only), true (archived repos only).'),
2291
+ 'include-forks': z
2267
2292
  .enum(['false', 'true', 'only'])
2268
2293
  .optional()
2269
- .describe('Include forks: false (exclude), true (include), only (forks only).'),
2294
+ .describe('Fork inclusion. "false" (exclude forks), "true" (include forks), "only" (forks only).'),
2270
2295
  visibility: z
2271
2296
  .enum(['public', 'private', 'internal'])
2272
2297
  .optional()
2273
- .describe('Repository visibility filter.'),
2298
+ .describe('Repository visibility.'),
2274
2299
  // DATE & SIZE FILTERS
2275
2300
  created: z
2276
2301
  .string()
2302
+ .regex(/^(>=?\d{4}-\d{2}-\d{2}|<=?\d{4}-\d{2}-\d{2}|\d{4}-\d{2}-\d{2}\.\.\d{4}-\d{2}-\d{2}|\d{4}-\d{2}-\d{2})$/, 'Invalid date format. Use: ">2020-01-01", ">=2020-01-01", "<2023-12-31", "<=2023-12-31", "2020-01-01..2023-12-31", or exact date "2023-01-01"')
2277
2303
  .optional()
2278
- .describe('Created date filter. Format: ">2020-01-01", "<2023-12-31".'),
2304
+ .describe('Repository creation date filter. Format: ">2020-01-01" (after), ">=2020-01-01" (on or after), "<2023-12-31" (before), "<=2023-12-31" (on or before), "2020-01-01..2023-12-31" (range), "2023-01-01" (exact).'),
2279
2305
  updated: z
2280
2306
  .string()
2307
+ .regex(/^(>=?\d{4}-\d{2}-\d{2}|<=?\d{4}-\d{2}-\d{2}|\d{4}-\d{2}-\d{2}\.\.\d{4}-\d{2}-\d{2}|\d{4}-\d{2}-\d{2})$/, 'Invalid date format. Use: ">2024-01-01", ">=2024-01-01", "<2022-01-01", "<=2022-01-01", "2023-01-01..2024-12-31", or exact date "2024-01-01"')
2281
2308
  .optional()
2282
- .describe('Updated date filter. Good for finding active projects.'),
2309
+ .describe('Last updated date filter. Format: ">2024-01-01" (recently updated), ">=2024-01-01" (on or after), "<2022-01-01" (not recently updated), "2023-01-01..2024-12-31" (range).'),
2283
2310
  size: z
2284
2311
  .string()
2312
+ .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid size format. Use: ">1000", ">=500", "<100", "<=50", "100..1000", or exact number "500"')
2285
2313
  .optional()
2286
- .describe('Repository size filter in KB. Format: ">1000", "<500".'),
2314
+ .describe('Repository size filter in KB. Format: ">1000" (large projects), ">=500" (medium-large), "<100" (small projects), "<=50" (tiny), "100..1000" (medium range), "500" (exact).'),
2287
2315
  // COMMUNITY FILTERS - Match CLI parameter names exactly
2288
- goodFirstIssues: z
2316
+ 'good-first-issues': z
2289
2317
  .union([
2290
2318
  z.number().int().min(0),
2291
2319
  z
@@ -2293,8 +2321,8 @@ function registerSearchGitHubReposTool(server) {
2293
2321
  .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: number, ">5", ">=10", "<20", "<=15", or "5..20"'),
2294
2322
  ])
2295
2323
  .optional()
2296
- .describe('Good first issues count. WORKING: Filter for beginner-friendly projects. EXCELLENT when combined with stars "100..5000" for quality beginner projects.'),
2297
- helpWantedIssues: z
2324
+ .describe('Good first issues count. Format: ">5" (many beginner issues), "1..10" (some beginner issues).'),
2325
+ 'help-wanted-issues': z
2298
2326
  .union([
2299
2327
  z.number().int().min(0),
2300
2328
  z
@@ -2302,13 +2330,24 @@ function registerSearchGitHubReposTool(server) {
2302
2330
  .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: number, ">5", ">=10", "<20", "<=15", or "5..20"'),
2303
2331
  ])
2304
2332
  .optional()
2305
- .describe('Help wanted issues count. Good for finding projects needing contributors.'),
2306
- followers: z.number().optional().describe('Followers count filter.'),
2307
- // SEARCH SCOPE
2333
+ .describe('Help wanted issues count. Format: ">10" (many help wanted), "1..5" (some help wanted).'),
2334
+ followers: z
2335
+ .union([
2336
+ z.number().int().min(0),
2337
+ z
2338
+ .string()
2339
+ .regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/, 'Invalid format. Use: ">1000", ">=500", "<100", "<=50", "100..1000", or exact number "500"'),
2340
+ ])
2341
+ .optional()
2342
+ .describe('Repository owner followers count. Format: ">1000" (popular developers), ">=500" (established developers), "<100" (smaller developers), "100..1000" (range).'),
2343
+ // SEARCH SCOPE - Match CLI exactly
2308
2344
  match: z
2309
- .enum(['name', 'description', 'readme'])
2345
+ .union([
2346
+ z.enum(['name', 'description', 'readme']),
2347
+ z.array(z.enum(['name', 'description', 'readme'])),
2348
+ ])
2310
2349
  .optional()
2311
- .describe('Search scope: name, description, or readme content.'),
2350
+ .describe('Search scope. "name" (repository names only), "description" (descriptions only), "readme" (README content). Can be single value or array.'),
2312
2351
  // SORTING & LIMITS - Match CLI defaults exactly
2313
2352
  sort: z
2314
2353
  .enum([
@@ -2320,7 +2359,7 @@ function registerSearchGitHubReposTool(server) {
2320
2359
  ])
2321
2360
  .optional()
2322
2361
  .default('best-match')
2323
- .describe('Sort criteria for results.'),
2362
+ .describe('Sort criteria.'),
2324
2363
  order: z
2325
2364
  .enum(['asc', 'desc'])
2326
2365
  .optional()
@@ -2333,7 +2372,7 @@ function registerSearchGitHubReposTool(server) {
2333
2372
  .max(100)
2334
2373
  .optional()
2335
2374
  .default(30)
2336
- .describe('Maximum results to return (1-100). Default: 30'),
2375
+ .describe('Maximum number of repositories to return (1-100).'),
2337
2376
  },
2338
2377
  annotations: {
2339
2378
  title: 'GitHub Repository Search',
@@ -2534,13 +2573,13 @@ function buildGitHubReposSearchCommand(params) {
2534
2573
  // CORE FILTERS
2535
2574
  addArg('owner', 'owner', !hasEmbeddedQualifiers);
2536
2575
  addArg('language', 'language', !hasEmbeddedQualifiers);
2537
- addArg('forks', 'forks', !hasEmbeddedQualifiers);
2576
+ addArg('forks', 'forks', !hasEmbeddedQualifiers, value => typeof value === 'number' ? value.toString() : value.trim());
2538
2577
  addArg('topic', 'topic', !hasEmbeddedQualifiers);
2539
- addArg('numberOfTopics', 'number-topics');
2578
+ addArg('number-topics', 'number-topics', true, value => typeof value === 'number' ? value.toString() : value.trim());
2540
2579
  addArg('stars', 'stars', !hasEmbeddedQualifiers, value => typeof value === 'number' ? value.toString() : value.trim());
2541
2580
  // QUALITY & STATE FILTERS
2542
2581
  addArg('archived', 'archived');
2543
- addArg('includeForks', 'include-forks');
2582
+ addArg('include-forks', 'include-forks');
2544
2583
  addArg('visibility', 'visibility');
2545
2584
  addArg('license', 'license');
2546
2585
  // DATE & SIZE FILTERS
@@ -2548,9 +2587,9 @@ function buildGitHubReposSearchCommand(params) {
2548
2587
  addArg('updated', 'updated');
2549
2588
  addArg('size', 'size');
2550
2589
  // COMMUNITY FILTERS
2551
- addArg('goodFirstIssues', 'good-first-issues', true, value => typeof value === 'number' ? value.toString() : value);
2552
- addArg('helpWantedIssues', 'help-wanted-issues', true, value => typeof value === 'number' ? value.toString() : value);
2553
- addArg('followers', 'followers');
2590
+ addArg('good-first-issues', 'good-first-issues', true, value => typeof value === 'number' ? value.toString() : value);
2591
+ addArg('help-wanted-issues', 'help-wanted-issues', true, value => typeof value === 'number' ? value.toString() : value);
2592
+ addArg('followers', 'followers', true, value => typeof value === 'number' ? value.toString() : value.trim());
2554
2593
  // SEARCH SCOPE
2555
2594
  addArg('match', 'match');
2556
2595
  // SORTING AND LIMITS
@@ -2563,8 +2602,258 @@ function buildGitHubReposSearchCommand(params) {
2563
2602
  return { command: 'search', args };
2564
2603
  }
2565
2604
 
2605
+ const GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME = 'githubViewRepoStructure';
2606
+ const DESCRIPTION$2 = `Browse GitHub repository file structure and navigate directories. Essential for exploring project layout, finding documentation, configuration files, and source code. Returns file/folder listings with size information. Automatically handles branch detection. Parameters: owner (required - GitHub username/org), repo (required - repository name), branch (required), path (optional - directory path).`;
2607
+ function registerViewRepositoryStructureTool(server) {
2608
+ server.registerTool(GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME, {
2609
+ description: DESCRIPTION$2,
2610
+ inputSchema: {
2611
+ owner: z
2612
+ .string()
2613
+ .min(1)
2614
+ .max(100)
2615
+ .regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/, 'Invalid GitHub username/org format')
2616
+ .describe('Repository owner/organization name (e.g., "facebook", "microsoft"). Do NOT include repository name.'),
2617
+ repo: z
2618
+ .string()
2619
+ .min(1)
2620
+ .max(100)
2621
+ .regex(/^[a-zA-Z0-9._-]+$/, 'Invalid repository name format')
2622
+ .describe(`Repository name under a organization. `),
2623
+ branch: z
2624
+ .string()
2625
+ .min(1)
2626
+ .max(255)
2627
+ .regex(/^[^\s]+$/, 'Branch name cannot contain spaces')
2628
+ .describe('Branch name (e.g., "main", "master", "develop"). Tool will automatically try default branch if specified branch is not found.'),
2629
+ path: z
2630
+ .string()
2631
+ .optional()
2632
+ .default('')
2633
+ .refine(path => !path.includes('..'), 'Path traversal not allowed')
2634
+ .refine(path => path.length <= 500, 'Path too long')
2635
+ .describe('Directory path within repository (e.g., "src", "docs", "src/components"). Leave empty for root directory. Do NOT start with slash.'),
2636
+ },
2637
+ annotations: {
2638
+ title: 'GitHub Repository Explorer',
2639
+ readOnlyHint: true,
2640
+ destructiveHint: false,
2641
+ idempotentHint: true,
2642
+ openWorldHint: true,
2643
+ },
2644
+ }, async (args) => {
2645
+ try {
2646
+ const result = await viewRepositoryStructure(args);
2647
+ return result;
2648
+ }
2649
+ catch (error) {
2650
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
2651
+ return createResult({
2652
+ error: `Failed to explore repository. ${errorMessage}. Verify repository exists and is accessible`,
2653
+ });
2654
+ }
2655
+ });
2656
+ }
2657
+ /**
2658
+ * Views the structure of a GitHub repository at a specific path.
2659
+ * Optimized for code analysis workflows with smart defaults and clear errors.
2660
+ */
2661
+ async function viewRepositoryStructure(params) {
2662
+ const cacheKey = generateCacheKey('gh-repo-structure', params);
2663
+ return withCache(cacheKey, async () => {
2664
+ const { owner, repo, branch, path = '' } = params;
2665
+ try {
2666
+ // Clean up path
2667
+ const cleanPath = path.startsWith('/') ? path.substring(1) : path;
2668
+ // Try the requested branch first, then fallback to main/master
2669
+ const branchesToTry = await getSmartBranchFallback(owner, repo, branch);
2670
+ let items = [];
2671
+ let usedBranch = branch;
2672
+ let lastError = null;
2673
+ let attemptCount = 0;
2674
+ const maxAttempts = branchesToTry.length;
2675
+ const triedBranches = [];
2676
+ for (const tryBranch of branchesToTry) {
2677
+ if (attemptCount >= maxAttempts)
2678
+ break;
2679
+ attemptCount++;
2680
+ triedBranches.push(tryBranch);
2681
+ try {
2682
+ const apiPath = `/repos/${owner}/${repo}/contents/${cleanPath}?ref=${tryBranch}`;
2683
+ const result = await executeGitHubCommand('api', [apiPath], {
2684
+ cache: false,
2685
+ });
2686
+ if (!result.isError) {
2687
+ const execResult = JSON.parse(result.content[0].text);
2688
+ const apiItems = execResult.result;
2689
+ items = Array.isArray(apiItems) ? apiItems : [apiItems];
2690
+ usedBranch = tryBranch;
2691
+ break;
2692
+ }
2693
+ else {
2694
+ lastError = new Error(result.content[0].text);
2695
+ }
2696
+ }
2697
+ catch (error) {
2698
+ lastError = error instanceof Error ? error : new Error(String(error));
2699
+ continue;
2700
+ }
2701
+ }
2702
+ if (items.length === 0) {
2703
+ // Check repository existence only after content fetch fails
2704
+ const repoCheckResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
2705
+ cache: false,
2706
+ });
2707
+ if (repoCheckResult.isError) {
2708
+ const repoErrorMsg = repoCheckResult.content[0].text;
2709
+ if (repoErrorMsg.includes('404')) {
2710
+ return createResult({
2711
+ error: `Repository "${owner}/${repo}" not found. It might have been deleted, renamed, or made private. Use github_search_code to find current location.`,
2712
+ });
2713
+ }
2714
+ else if (repoErrorMsg.includes('403')) {
2715
+ return createResult({
2716
+ error: `Repository "${owner}/${repo}" exists but access is denied. Repository might be private or archived. Use api_status_check to verify permissions.`,
2717
+ });
2718
+ }
2719
+ }
2720
+ const errorMsg = lastError?.message || 'Unknown error';
2721
+ if (errorMsg.includes('404') || errorMsg.includes('Not Found')) {
2722
+ if (path) {
2723
+ const searchSuggestion = await suggestPathSearchFallback(owner, path);
2724
+ return createResult({
2725
+ error: `Path "${path}" not found in any branch (tried: ${triedBranches.join(', ')}).${searchSuggestion}`,
2726
+ });
2727
+ }
2728
+ else {
2729
+ return createResult({
2730
+ error: `Repository "${owner}/${repo}" structure not accessible. Repository might be empty, private, or you might not have sufficient permissions. Use github_search_code with owner="${owner}" to find accessible repositories.`,
2731
+ });
2732
+ }
2733
+ }
2734
+ else if (errorMsg.includes('403') || errorMsg.includes('Forbidden')) {
2735
+ return createResult({
2736
+ error: `Access denied to "${owner}/${repo}". Repository exists but might be private/archived. Use api_status_check to verify permissions, or github_search_code with owner="${owner}" to find accessible repositories.`,
2737
+ });
2738
+ }
2739
+ else {
2740
+ const searchSuggestion = path
2741
+ ? await suggestPathSearchFallback(owner, path)
2742
+ : '';
2743
+ return createResult({
2744
+ error: `Failed to access "${owner}/${repo}": ${errorMsg}. Check network connection and repository permissions.${searchSuggestion}`,
2745
+ });
2746
+ }
2747
+ }
2748
+ // Limit total items to 100 for efficiency
2749
+ const limitedItems = items.slice(0, 100);
2750
+ // Sort: directories first, then alphabetically
2751
+ limitedItems.sort((a, b) => {
2752
+ if (a.type !== b.type) {
2753
+ return a.type === 'dir' ? -1 : 1;
2754
+ }
2755
+ return a.name.localeCompare(b.name);
2756
+ });
2757
+ // Create simplified, token-efficient structure
2758
+ const files = limitedItems
2759
+ .filter(item => item.type === 'file')
2760
+ .map(item => ({
2761
+ name: item.name,
2762
+ size: item.size,
2763
+ url: item.path, // Use path for fetching
2764
+ }));
2765
+ const folders = limitedItems
2766
+ .filter(item => item.type === 'dir')
2767
+ .map(item => ({
2768
+ name: item.name,
2769
+ url: item.path, // Use path for browsing
2770
+ }));
2771
+ return createResult({
2772
+ data: {
2773
+ repository: `${owner}/${repo}`,
2774
+ branch: usedBranch,
2775
+ path: cleanPath || '/',
2776
+ githubBasePath: `https://api.github.com/repos/${owner}/${repo}/contents/`,
2777
+ files: {
2778
+ count: files.length,
2779
+ files: files,
2780
+ },
2781
+ folders: {
2782
+ count: folders.length,
2783
+ folders: folders,
2784
+ },
2785
+ },
2786
+ });
2787
+ }
2788
+ catch (error) {
2789
+ const errorMessage = error instanceof Error ? error.message : String(error);
2790
+ return createResult({
2791
+ error: `Failed to access repository "${owner}/${repo}": ${errorMessage}. Verify repository name, permissions, and network connection.`,
2792
+ });
2793
+ }
2794
+ });
2795
+ }
2796
+ /**
2797
+ * Smart branch detection with automatic fallback to common branch names.
2798
+ * Now includes more comprehensive branch detection and better error handling.
2799
+ */
2800
+ async function getSmartBranchFallback(owner, repo, requestedBranch) {
2801
+ const branches = new Set([requestedBranch]);
2802
+ try {
2803
+ // Try to get repository info to find default branch
2804
+ const repoInfoResult = await executeGitHubCommand('api', [`/repos/${owner}/${repo}`], {
2805
+ cache: false,
2806
+ });
2807
+ if (!repoInfoResult.isError) {
2808
+ const execResult = JSON.parse(repoInfoResult.content[0].text);
2809
+ const repoData = execResult.result;
2810
+ const defaultBranch = repoData.default_branch;
2811
+ if (defaultBranch) {
2812
+ branches.add(defaultBranch);
2813
+ }
2814
+ }
2815
+ }
2816
+ catch {
2817
+ // If we can't get repo info, proceed with standard fallbacks
2818
+ }
2819
+ // Add only main/master as fallback branches
2820
+ const commonBranches = ['main', 'master'];
2821
+ commonBranches.forEach(branch => branches.add(branch));
2822
+ // Convert Set back to array, with requested branch first
2823
+ const branchesArray = Array.from(branches);
2824
+ branchesArray.sort((a, b) => {
2825
+ if (a === requestedBranch)
2826
+ return -1;
2827
+ if (b === requestedBranch)
2828
+ return 1;
2829
+ return 0;
2830
+ });
2831
+ return branchesArray;
2832
+ }
2833
+ // Helper function to suggest path search strategy
2834
+ async function suggestPathSearchFallback(owner, path) {
2835
+ try {
2836
+ // Extract last path segment and try to find in same organization
2837
+ const pathSegment = path.split('/').pop() || path;
2838
+ const searchResult = await executeGitHubCommand('api', [
2839
+ `/search/code?q=${encodeURIComponent(pathSegment)}+in:path+org:${owner}`,
2840
+ ], { cache: false });
2841
+ if (!searchResult.isError) {
2842
+ const results = JSON.parse(searchResult.content[0].text);
2843
+ if (results.total_count > 0) {
2844
+ const firstMatch = results.items[0];
2845
+ return ` Directory might be in ${firstMatch.repository.full_name}. Try these searches:\n1. github_search_code with query="${pathSegment}" owner="${owner}"\n2. github_search_code with query="path:${path}" owner="${owner}"`;
2846
+ }
2847
+ }
2848
+ }
2849
+ catch {
2850
+ // Fallback to generic message if search fails
2851
+ }
2852
+ return ` Try these searches:\n1. github_search_code with query="${path.split('/').pop()}" owner="${owner}"\n2. github_search_code with query="path:${path}"`;
2853
+ }
2854
+
2566
2855
  const NPM_PACKAGE_SEARCH_TOOL_NAME = 'npmPackageSearch';
2567
- 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).`;
2856
+ const DESCRIPTION$1 = `Search NPM packages by name or functionality keywords. Supports multiple search terms for comprehensive package discovery. Returns package name, version, description, keywords, and repository information. Best for finding JavaScript/TypeScript libraries and tools.`;
2568
2857
  const MAX_DESCRIPTION_LENGTH = 100;
2569
2858
  const MAX_KEYWORDS = 10;
2570
2859
  function registerNpmSearchTool(server) {
@@ -2573,7 +2862,7 @@ function registerNpmSearchTool(server) {
2573
2862
  inputSchema: {
2574
2863
  queries: z
2575
2864
  .union([z.string(), z.array(z.string())])
2576
- .describe('Search terms for packages. Use functionality keywords: "react hooks", "cli tool", "testing"'),
2865
+ .describe('Search terms for NPM packages (e.g., "react hooks", ["typescript", "eslint"], "data visualization"). Use functionality keywords rather than exact package names for best results.'),
2577
2866
  searchLimit: z
2578
2867
  .number()
2579
2868
  .int()
@@ -2667,13 +2956,12 @@ function parseNpmSearchOutput(output) {
2667
2956
  return packages.map(normalizePackage);
2668
2957
  }
2669
2958
  catch (error) {
2670
- logger.warn('Failed to parse NPM search results:', error);
2671
2959
  return [];
2672
2960
  }
2673
2961
  }
2674
2962
 
2675
2963
  const NPM_VIEW_PACKAGE_TOOL_NAME = 'npmViewPackage';
2676
- 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).`;
2964
+ const DESCRIPTION = `Get detailed NPM package information including version, description, repository URL, size, and download statistics. Essential for understanding package details before installation. Returns optimized metadata for package evaluation and code navigation.`;
2677
2965
  function registerNpmViewPackageTool(server) {
2678
2966
  server.registerTool(NPM_VIEW_PACKAGE_TOOL_NAME, {
2679
2967
  description: DESCRIPTION,
@@ -2681,7 +2969,7 @@ function registerNpmViewPackageTool(server) {
2681
2969
  packageName: z
2682
2970
  .string()
2683
2971
  .min(1)
2684
- .describe('NPM package name (e.g., "react", "express", "@types/node")'),
2972
+ .describe('NPM package name (e.g., "react", "express", "@types/node"). Include @ prefix for scoped packages.'),
2685
2973
  },
2686
2974
  annotations: {
2687
2975
  title: 'NPM Package Analyzer',
@@ -2831,133 +3119,125 @@ using gh cli for github and npm cli for packages.
2831
3119
 
2832
3120
  CRITICAL SEARCH PRINCIPLES:
2833
3121
 
2834
- ## 1. PROGRESSIVE SEARCH STRATEGY (MOST IMPORTANT):
2835
- a) START BROAD: Begin with simple, general terms (1-2 words max)
2836
- b) ANALYZE RESULTS: Learn from what you find (repo names, owners, common patterns)
2837
- c) REFINE GRADUALLY: Add filters only after understanding the landscape
3122
+ PROGRESSIVE SEARCH STRATEGY (MOST IMPORTANT):
3123
+ a) START BROAD: Begin with simple, general queries and terms (1-2 words max)
3124
+ b) ANALYZE RESULTS: Learn from what you find
3125
+ c) REFINE GRADUALLY: Add filters only after understanding the landscape and if needed (only if the user asks for it explicitly)
2838
3126
  d) MULTIPLE ANGLES: Try different search terms if first approach yields no results
2839
3127
 
2840
- ## 2. SEARCH PROGRESSION EXAMPLES:
2841
- - User asks about "React state management"
2842
- 1st search: "state" or "redux" (BROAD)
2843
- 2nd search: "react state" with language:javascript (REFINED)
2844
- 3rd search: owner:facebook with specific terms (TARGETED)
2845
-
2846
- - User asks about "authentication libraries"
2847
- 1st search: "auth" or "authentication" (BROAD)
2848
- 2nd search: Add language filter based on results
2849
- 3rd search: Focus on specific owners/topics found
3128
+ HANDLING NO RESULTS:
3129
+ - NEVER give up after one failed search.
3130
+ - Act on fallbacks messages and try to understand the user's intent.
3131
+ - Try progressively BROADER terms (simplify queries and remove filters)
2850
3132
 
2851
- ## 3. HANDLING NO RESULTS:
2852
- - NEVER give up after one failed search
2853
- - Try progressively BROADER terms
2854
- - Remove ALL filters and try core keywords
2855
- - Search for related concepts (e.g., "auth" → "login" → "session")
2856
- - Check different owners/organizations
2857
- - For code search: try searching in popular repos first
2858
-
2859
- ## 4. SMART FILTER USAGE:
3133
+ SMART FILTER USAGE:
2860
3134
  - NO FILTERS on first search (unless user specifies)
2861
3135
  - Add ONE filter at a time based on results
2862
- - Common progression: query → +language → +stars → +owner
3136
+ - Common progression: query → +language → +stars → +owner (only if user asks for it explicitly)
2863
3137
  - Reserve complex filters for final refinement
2864
3138
 
2865
- ## 5. TOOL SYNERGY:
2866
- - Use repository search to find relevant repos FIRST
2867
- - Then use code search within those repos
2868
- - Check npm packages for JavaScript/TypeScript queries
2869
- - Verify access with api_status_check for private repos
2870
-
2871
- ## 6. RESEARCH BEST PRACTICES:
3139
+ RESEARCH BEST PRACTICES:
2872
3140
  - Conduct COMPREHENSIVE research with multiple searches
2873
3141
  - Learn from each search to improve the next
2874
3142
  - Provide context about your search strategy
2875
3143
  - Always verify technical details with actual code
2876
3144
 
2877
- ## 7. COMPLEX ANALYSIS PATTERNS:
3145
+ COMPLEX ANALYSIS PATTERNS:
2878
3146
 
2879
- ### Multi-Framework Comparison (e.g., React vs Vue):
2880
- 1. Search repos separately: "react", then "vue"
2881
- 2. Find core implementation files: "scheduler" in React, "reactivity" in Vue
2882
- 3. Search specific features: "concurrent", "fiber", "proxy"
3147
+ Multi-terms Comparison
3148
+ 1. Search repos separately: "repoA", then "repoB"
3149
+ 2. Review repositories structure and files
3150
+ 3. Review code and documentation
2883
3151
  4. Compare similar functionalities across repos
3152
+ 5. Use filters and flags to narrow down the results
2884
3153
 
2885
- ### Architecture Analysis (e.g., State Management):
2886
- 1. Start broad: "state" or "store"
2887
- 2. Identify major libraries: Redux, Zustand, Jotai
2888
- 3. Search implementation patterns: "reducer", "atom", "selector"
2889
- 4. Analyze each approach separately, then compare
2890
-
2891
- ### Evolution Tracking (e.g., Feature History):
3154
+ Evolution Tracking (e.g., Feature History):
2892
3155
  1. Use commit search with broad terms first
2893
3156
  2. Narrow by date ranges progressively
2894
3157
  3. Track changes in specific files over time
2895
3158
  4. Identify key contributors and their patterns
2896
3159
 
2897
- ### Performance Analysis:
2898
- 1. Search for benchmarks: "benchmark", "perf", "performance"
2899
- 2. Look for optimization commits: "optimize", "faster", "improve"
2900
- 3. Find profiling code: "profile", "measure", "timing"
2901
- 4. Compare implementation strategies
2902
-
2903
- ## 8. CROSS-REPOSITORY INTELLIGENCE:
3160
+ CROSS-REPOSITORY INTELLIGENCE:
2904
3161
  - When comparing frameworks, search EACH separately first
2905
3162
  - Build mental model of each codebase structure
2906
- - Use discovered patterns to refine searches
2907
- - Connect findings across repositories for insights
3163
+ - Use discovered patterns to refine searches and connect findings across repositories for insights
3164
+
3165
+ RESEARCH WORKFLOW BEST PRACTICES:
3166
+
3167
+ Discovery Phase (Unknown Projects/Topics):
3168
+ - START with ${GITHUB_SEARCH_REPOSITORIES_TOOL_NAME} using TOPICS
3169
+ - Topics are underutilized but extremely powerful for discovery
3170
+ - Example: { topic: ["react", "typescript", "testing"], stars: ">500" }
3171
+
3172
+ Understanding Phase
3173
+ - ALWAYS use ${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME} first to verify repository exists
3174
+ - CRITICAL: Many failures are due to incorrect repository names - verify before proceeding
3175
+ - Check README.md, docs/, configuration files
3176
+ - Understand project structure before searching code
3177
+ - Never make assumptions about repository names or file locations - always verify
3178
+
3179
+ Implementation Search (Specific Code):
3180
+ - Fetch importnat files using ${GITHUB_GET_FILE_CONTENT_TOOL_NAME}
3181
+ - Use ${GITHUB_SEARCH_CODE_TOOL_NAME} with NARROW queries
3182
+ - Start precise, broaden only if needed
3183
+ - Use exact phrases when you know patterns
3184
+ - Verify dependences and imports
3185
+
3186
+ File Access Validation Workflow:
3187
+ 1. VERIFY repository name first with ${GITHUB_SEARCH_CODE_TOOL_NAME} if unsure
3188
+ 2. Use ${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME} to confirm repository structure
3189
+ 3. Navigate to parent directory first to understand layout
3190
+ 4. Only then use ${GITHUB_GET_FILE_CONTENT_TOOL_NAME} with confirmed paths
3191
+ 5. Most "file not found" errors are due to incorrect repository names
2908
3192
 
2909
- ## 9. TOOL-SPECIFIC BEST PRACTICES:
3193
+ TOOL-SPECIFIC BEST PRACTICES:
2910
3194
 
2911
- ### ${API_STATUS_CHECK_TOOL_NAME}:
3195
+ ${API_STATUS_CHECK_TOOL_NAME}:
2912
3196
  - Run FIRST when dealing with private repositories
2913
3197
  - Use organizations list to scope searches
2914
3198
  - Verify authentication before extensive searches
2915
3199
 
2916
- ### ${GITHUB_SEARCH_REPOSITORIES_TOOL_NAME}:
3200
+ ${GITHUB_SEARCH_REPOSITORIES_TOOL_NAME}:
2917
3201
  - Start with topic/language, add stars/forks filters later
2918
3202
  - Use date ranges for trending analysis
2919
3203
  - Combine multiple searches for comprehensive discovery
2920
3204
 
2921
- ### ${GITHUB_SEARCH_CODE_TOOL_NAME}:
3205
+ ${GITHUB_SEARCH_CODE_TOOL_NAME}:
2922
3206
  - Begin with function/class names, not full signatures
2923
3207
  - Use extension filters for targeted searches
2924
3208
  - Try partial matches before exact phrases
2925
3209
 
2926
- ### ${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME}:
3210
+ ${GITHUB_VIEW_REPO_STRUCTURE_TOOL_NAME}:
2927
3211
  - Navigate from root, then drill down
2928
3212
  - Use for understanding project organization
2929
3213
  - Check common paths: src/, lib/, packages/
2930
3214
 
2931
- ### ${GITHUB_GET_FILE_CONTENT_TOOL_NAME}:
3215
+ ${GITHUB_GET_FILE_CONTENT_TOOL_NAME}:
2932
3216
  - Verify file paths with repo structure first
2933
3217
  - Use for implementation details and documentation
2934
- - Remember 300KB limit for large files
2935
3218
 
2936
- ### ${GITHUB_SEARCH_COMMITS_TOOL_NAME}:
2937
- - Search by feature keywords, not commit hashes
2938
- - Use author filter for contributor analysis
3219
+ ${GITHUB_SEARCH_COMMITS_TOOL_NAME}:
3220
+ - Search by feature keywords
2939
3221
  - Date ranges help track feature evolution
2940
3222
 
2941
- ### ${GITHUB_SEARCH_ISSUES_TOOL_NAME} & ${GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME}:
2942
- - Search for problem descriptions, not solutions
3223
+ ${GITHUB_SEARCH_ISSUES_TOOL_NAME} & ${GITHUB_SEARCH_PULL_REQUESTS_TOOL_NAME}:
3224
+ - Search for problem descriptions
2943
3225
  - Use state filters progressively
2944
- - Labels reveal project categorization
2945
3226
 
2946
- ### ${NPM_PACKAGE_SEARCH_TOOL_NAME}:
2947
- - Use functional terms: "router", "validator", "parser"
2948
- - Search multiple related terms in parallel
2949
- - Aggregate results for comprehensive view
3227
+ ${NPM_PACKAGE_SEARCH_TOOL_NAME}:
3228
+ - Search npm package in registry - use one term at a time (partial or exact)
2950
3229
 
2951
- ### ${NPM_VIEW_PACKAGE_TOOL_NAME}:
2952
- - Check repository field for source code access
2953
- - Review exports for API understanding
2954
- - Use version history to gauge stability
3230
+ ${NPM_VIEW_PACKAGE_TOOL_NAME}:
3231
+ - Returns essential data like github path and package metadata like dependencies, exports...
2955
3232
 
2956
- ## 10. CHAIN OF THOUGHT OPTIMIZATION:
3233
+ CHAIN OF THOUGHT OPTIMIZATION:
2957
3234
  - Plan search sequence before executing
2958
3235
  - Document reasoning for each search refinement
2959
3236
  - Build knowledge progressively, don't jump to specifics
2960
3237
  - Validate findings with multiple sources
3238
+ - Ask user for clarification if needed
3239
+ - Learn from results and refine search strategy
3240
+ - Output high level summary of the search and results based on data and not assumptions
2961
3241
  `;
2962
3242
 
2963
3243
  const SERVER_CONFIG = {
@@ -2996,39 +3276,31 @@ function registerAllTools(server) {
2996
3276
  },
2997
3277
  { name: NPM_VIEW_PACKAGE_TOOL_NAME, fn: registerNpmViewPackageTool },
2998
3278
  ];
2999
- logger.info(`Registering ${toolRegistrations.length} tools...`);
3000
3279
  let successCount = 0;
3001
3280
  for (const tool of toolRegistrations) {
3002
3281
  try {
3003
- logger.debug(`Registering tool: ${tool.name}`);
3004
3282
  tool.fn(server);
3005
3283
  successCount++;
3006
- logger.info(`✓ Successfully registered: ${tool.name}`);
3007
3284
  }
3008
3285
  catch (error) {
3009
- logger.error(`✗ Failed to register ${tool.name}:`, error);
3010
3286
  // Continue with other tools instead of failing completely
3011
3287
  }
3012
3288
  }
3013
3289
  if (successCount === 0) {
3014
3290
  throw new Error('No tools were successfully registered');
3015
3291
  }
3016
- logger.info(`All tools registration completed - ${successCount}/${toolRegistrations.length} successful`);
3017
3292
  }
3018
3293
  async function startServer() {
3019
3294
  try {
3020
- logger.info('Creating MCP server...');
3021
3295
  const server = new McpServer(SERVER_CONFIG);
3022
3296
  registerAllTools(server);
3023
3297
  const transport = new StdioServerTransport();
3024
3298
  await server.connect(transport);
3025
- logger.info('=== Server Connected Successfully ===');
3026
3299
  // Ensure all buffered output is sent
3027
3300
  process.stdout.uncork();
3028
3301
  process.stderr.uncork();
3029
- const gracefulShutdown = async (signal) => {
3302
+ const gracefulShutdown = async () => {
3030
3303
  try {
3031
- logger.info(`Received ${signal}, shutting down gracefully...`);
3032
3304
  clearAllCache();
3033
3305
  // Give server time to close properly
3034
3306
  await Promise.race([
@@ -3038,35 +3310,30 @@ async function startServer() {
3038
3310
  process.exit(0);
3039
3311
  }
3040
3312
  catch (error) {
3041
- logger.error('Error during shutdown:', error);
3042
3313
  process.exit(1);
3043
3314
  }
3044
3315
  };
3045
3316
  // Handle process signals
3046
- process.on('SIGINT', () => gracefulShutdown('SIGINT'));
3047
- process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
3317
+ process.on('SIGINT', gracefulShutdown);
3318
+ process.on('SIGTERM', gracefulShutdown);
3048
3319
  // Handle stdin close (important for MCP)
3049
3320
  process.stdin.on('close', async () => {
3050
- await gracefulShutdown('STDIN_CLOSE');
3321
+ await gracefulShutdown();
3051
3322
  });
3052
3323
  // Handle uncaught errors
3053
- process.on('uncaughtException', error => {
3054
- logger.error('Uncaught exception:', error);
3055
- gracefulShutdown('UNCAUGHT_EXCEPTION');
3324
+ process.on('uncaughtException', () => {
3325
+ gracefulShutdown();
3056
3326
  });
3057
- process.on('unhandledRejection', (reason, promise) => {
3058
- logger.error('Unhandled rejection at:', promise, 'reason:', reason);
3059
- gracefulShutdown('UNHANDLED_REJECTION');
3327
+ process.on('unhandledRejection', () => {
3328
+ gracefulShutdown();
3060
3329
  });
3061
3330
  // Keep process alive
3062
3331
  process.stdin.resume();
3063
3332
  }
3064
3333
  catch (error) {
3065
- logger.error('Error details:', error);
3066
3334
  process.exit(1);
3067
3335
  }
3068
3336
  }
3069
- startServer().catch(error => {
3070
- logger.error('Error:', error);
3337
+ startServer().catch(() => {
3071
3338
  process.exit(1);
3072
3339
  });