octocode-mcp 2.3.0 → 2.3.2

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 (3) hide show
  1. package/README.md +133 -515
  2. package/build/index.js +434 -385
  3. package/package.json +27 -27
package/build/index.js CHANGED
@@ -7,79 +7,40 @@ import NodeCache from 'node-cache';
7
7
  import crypto from 'crypto';
8
8
  import z from 'zod';
9
9
 
10
- const TOOL_NAMES = {
11
- // System & API Status
12
- API_STATUS_CHECK: 'api_status_check',
13
- // GitHub
14
- GITHUB_SEARCH_CODE: 'github_search_code',
15
- GITHUB_SEARCH_REPOS: 'github_search_repositories',
16
- GITHUB_SEARCH_COMMITS: 'github_search_commits',
17
- GITHUB_SEARCH_ISSUES: 'github_search_issues',
18
- GITHUB_SEARCH_PULL_REQUESTS: 'github_search_pull_requests',
19
- GITHUB_GET_CONTENTS: 'github_get_contents',
20
- GITHUB_GET_FILE_CONTENT: 'github_get_file_content',
21
- // NPM
22
- NPM_PACKAGE_SEARCH: 'npm_package_search',
23
- NPM_VIEW_PACKAGE: 'npm_view_package',
24
- };
25
- const PROMPT_SYSTEM_PROMPT = `Smart code research assistant with semantic search capabilities.
10
+ const PROMPT_SYSTEM_PROMPT = `You are an expert code research assistant for developers doing smart research in GitHub and NPM ecosystems (public/private).
11
+ You leverage powerful semantic search using GitHub (gh) and NPM CLI for code discovery.
12
+
13
+ IMPORTANT: check users github organizations and use them in github search tools if needed
14
+
15
+ TOOLS:
16
+ - API status: Check npm/gh connectivity and find user's GitHub organizations (for private repo access)
17
+ - GitHub: Search code, repositories, issues, pull requests, commits
18
+ - NPM: Search packages, view metadata (git URL, exports, dependencies, versions with dates)
26
19
 
27
20
  APPROACH:
28
- - Start with ${TOOL_NAMES.API_STATUS_CHECK} to check authentication
29
- - Use ${TOOL_NAMES.GITHUB_SEARCH_REPOS} for smart repository and topic discovery
30
- - Run searches in parallel when possible for efficiency
31
- - Dive deeper with specific tools for detailed analysis
21
+ - Once code/project path is found from tools use it and research it for more data
22
+ - Understand queries semantically to choose optimal tools
23
+ - Optimize tools calls data and be smart about it (e.g. is some tool get information don't use other tools to get the same information)
24
+ - Prioritize efficient, targeted searches with smart fallbacks
25
+ - Use strategic tool combinations for comprehensive results an
26
+ - Balance speed vs throughness based on query type
32
27
 
33
- SEARCH STRATEGY:
34
- - ${TOOL_NAMES.GITHUB_SEARCH_REPOS} - Smart semantic search combining repositories and topics
35
- - ${TOOL_NAMES.GITHUB_SEARCH_CODE} - Find implementation patterns and examples
36
- - ${TOOL_NAMES.NPM_PACKAGE_SEARCH} - Package ecosystem discovery
37
- - ${TOOL_NAMES.NPM_VIEW_PACKAGE} - Complete package analysis with exports for file discovery
38
- - ${TOOL_NAMES.GITHUB_SEARCH_ISSUES} + ${TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS} - Understanding development patterns
39
- - ${TOOL_NAMES.GITHUB_GET_CONTENTS} → ${TOOL_NAMES.GITHUB_GET_FILE_CONTENT} - ALWAYS verify file existence before fetching
28
+ GITHUB SEARCH STRATEGY:
29
+ - OR: Explore alternatives ("useState OR setState OR setData")
30
+ - AND: Precise requirements ("react AND testing AND hooks")
31
+ - NOT: Filter noise ("auth NOT test NOT mock")
32
+ - Quotes: Exact phrases ("import React", "useEffect cleanup")
33
+ - Mix with filters: language, path, repo for laser focus
40
34
 
41
- BEST PRACTICES:
42
- - Use natural language queries - the tools are semantic and adaptive
43
- - Leverage quality filters like stars:>100 for established projects
44
- - Run parallel searches for comprehensive results
45
- - ALWAYS verify file existence with ${TOOL_NAMES.GITHUB_GET_CONTENTS} before using ${TOOL_NAMES.GITHUB_GET_FILE_CONTENT}
46
- - Use ${TOOL_NAMES.NPM_VIEW_PACKAGE} exports field to discover available files in packages
47
- - Always check documentation and examples when available`;
48
- const TOOL_DESCRIPTIONS = {
49
- [TOOL_NAMES.API_STATUS_CHECK]: `Check GitHub & NPM authentication status and discover user organizations.
50
- Essential first step that enables access to private/organizational repositories by identifying available organizations for the 'owner' parameter in search tools.
51
- Critical for enterprise code exploration and accessing company-specific repositories that require organizational membership.`,
52
- [TOOL_NAMES.NPM_PACKAGE_SEARCH]: `Search NPM packages by keyword. Use for package ecosystem discovery.`,
53
- [TOOL_NAMES.NPM_VIEW_PACKAGE]: `Get comprehensive package metadata essential for GitHub searches and code analysis. Returns complete package context:
54
- • repositoryGitUrl - Direct GitHub repo link for accurate searches
55
- • exports - Critical for discovering available files and import paths
56
- • dependencies/devDependencies - Full ecosystem understanding
57
- • versions with dates - Historical evolution context
58
- The exports field is invaluable for GitHub file discovery - shows exact paths before fetching. Always use when finding packages in code.`,
59
- [TOOL_NAMES.GITHUB_SEARCH_CODE]: `SEMANTIC CODE DISCOVERY: Search code with boolean logic (AND, OR, NOT).
60
- Format: "term AND term" language:js path:src. Filters: owner/org/user, repo, extension, filename, language, path, size, limit, match scope.
61
- Use for finding actual implementation patterns and code examples.
62
- CRITICAL: When packages found in results or from user input, use ${TOOL_NAMES.NPM_VIEW_PACKAGE} for metadata/paths.`,
63
- [TOOL_NAMES.GITHUB_SEARCH_REPOS]: `Search repositories by name/description. PRIMARY FILTERS work alone: owner, language, stars, topic, forks. SECONDARY FILTERS require query/primary filter: license, created, archived, includeForks, updated, visibility, match.
64
-
65
- PATTERNS: Use topic:["cli","typescript"] for semantic discovery. Use stars:">100" for quality. Use owner:"microsoft" for organization repos. Query supports GitHub syntax: "language:Go OR language:Rust".
66
-
67
- CRITICAL: When finding packages, use ${TOOL_NAMES.NPM_VIEW_PACKAGE} for metadata and repository paths.`,
68
- [TOOL_NAMES.GITHUB_GET_CONTENTS]: `Browse repository structure and verify file existence. ALWAYS use before github_get_file_content to confirm files exist and understand organization.`,
69
- [TOOL_NAMES.GITHUB_GET_FILE_CONTENT]: `Read file content. REQUIRES exact path verification from github_get_contents first. If fetching fails, check file existence with github_get_contents.`,
70
- [TOOL_NAMES.GITHUB_SEARCH_ISSUES]: `Find GitHub issues and problems with rich metadata (labels, reactions, comments, state).
71
- Discover pain points, feature requests, bug patterns, and community discussions.
72
- Filter by state, labels, assignee, or date ranges. Use for understanding project health and common user issues.`,
73
- [TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS]: `Find pull requests and implementations with detailed metadata.
74
- Discover how features were implemented, code review patterns, and development workflows.
75
- Filter by state, author, reviewer, or merge status. Essential for understanding project development practices.`,
76
- [TOOL_NAMES.GITHUB_SEARCH_COMMITS]: `Search commit history. Use for understanding code evolution and development patterns.`,
77
- };
35
+ GUIDELINES:
36
+ - Discovery queries ("How X works?"): Be comprehensive, use multiple tools strategically
37
+ - Direct queries ("Where is X package repo?"): Use quick, targeted approach
38
+ - Always provide referenced code snippets and documentation
39
+ - Search docs (README.md) for quality information about code flow and architecture`;
78
40
 
79
- // CONSOLIDATED ERROR & SUCCESS HANDLING
80
41
  function createResult(data, isError = false, suggestions) {
81
42
  const text = isError
82
- ? `${data}${suggestions ? ` | Try: ${suggestions.join(', ')}` : ''}`
43
+ ? `${data}${''}`
83
44
  : JSON.stringify(data, null, 2);
84
45
  return {
85
46
  content: [{ type: 'text', text }],
@@ -103,56 +64,6 @@ function parseJsonResponse(responseText, fallback = null) {
103
64
  return { data: (fallback || responseText), parsed: false };
104
65
  }
105
66
  }
106
- /**
107
- * Generate fallback suggestions for no results - ensures no tool suggests itself
108
- */
109
- function getNoResultsSuggestions(currentTool) {
110
- const suggestions = [];
111
- // Tool-specific fallbacks
112
- switch (currentTool) {
113
- case TOOL_NAMES.GITHUB_SEARCH_REPOS:
114
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_CODE, TOOL_NAMES.NPM_PACKAGE_SEARCH);
115
- break;
116
- case TOOL_NAMES.GITHUB_SEARCH_CODE:
117
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_REPOS, TOOL_NAMES.GITHUB_SEARCH_ISSUES);
118
- break;
119
- case TOOL_NAMES.NPM_PACKAGE_SEARCH:
120
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_REPOS, TOOL_NAMES.GITHUB_SEARCH_CODE);
121
- break;
122
- case TOOL_NAMES.GITHUB_SEARCH_ISSUES:
123
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_CODE, TOOL_NAMES.GITHUB_SEARCH_REPOS);
124
- break;
125
- case TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS:
126
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_ISSUES, TOOL_NAMES.GITHUB_SEARCH_CODE);
127
- break;
128
- case TOOL_NAMES.GITHUB_SEARCH_COMMITS:
129
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_CODE, TOOL_NAMES.GITHUB_SEARCH_REPOS);
130
- break;
131
- case TOOL_NAMES.GITHUB_GET_CONTENTS:
132
- case TOOL_NAMES.GITHUB_GET_FILE_CONTENT:
133
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_REPOS, TOOL_NAMES.GITHUB_SEARCH_CODE);
134
- break;
135
- default:
136
- // Fallback for any other tools
137
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_REPOS, TOOL_NAMES.GITHUB_SEARCH_CODE);
138
- }
139
- return suggestions.slice(0, 3);
140
- }
141
- /**
142
- * Generate fallback suggestions for errors - ensures no tool suggests itself
143
- */
144
- function getErrorSuggestions(currentTool) {
145
- const suggestions = [];
146
- // Always suggest API status check first (unless it's the current tool)
147
- if (currentTool !== TOOL_NAMES.API_STATUS_CHECK) {
148
- suggestions.push(TOOL_NAMES.API_STATUS_CHECK);
149
- }
150
- // Add discovery alternatives
151
- if (currentTool !== TOOL_NAMES.GITHUB_SEARCH_REPOS) {
152
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_REPOS);
153
- }
154
- return suggestions.slice(0, 3);
155
- }
156
67
  /**
157
68
  * Determines if a string needs quoting for GitHub search
158
69
  */
@@ -231,8 +142,9 @@ async function executeNpmCommand(command, args = [], options = {}) {
231
142
  if (!isValidNpmCommand(command)) {
232
143
  return createErrorResult('Command not registered', new Error(`NPM command '${command}' is not in the allowed list`));
233
144
  }
234
- // Build command with validated prefix - no sanitization needed since we control the prefix
235
- const fullCommand = `npm ${command} ${args.join(' ')}`;
145
+ // Build command with validated prefix and properly escaped arguments
146
+ const escapedArgs = args.map(escapeShellArg);
147
+ const fullCommand = `npm ${command} ${escapedArgs.join(' ')}`;
236
148
  const executeNpmCommand = () => executeCommand(fullCommand, 'npm', options);
237
149
  if (options.cache) {
238
150
  const cacheKey = generateCacheKey('npm-exec', { command, args });
@@ -240,6 +152,17 @@ async function executeNpmCommand(command, args = [], options = {}) {
240
152
  }
241
153
  return executeNpmCommand();
242
154
  }
155
+ /**
156
+ * Escape shell arguments to prevent shell injection and handle special characters
157
+ */
158
+ function escapeShellArg(arg) {
159
+ // If the argument contains special characters, wrap it in single quotes
160
+ // and escape any single quotes within the argument
161
+ if (/[^\w\-._/:=@]/.test(arg)) {
162
+ return `'${arg.replace(/'/g, "'\"'\"'")}'`;
163
+ }
164
+ return arg;
165
+ }
243
166
  /**
244
167
  * Execute GitHub CLI commands safely by validating against allowed commands
245
168
  * Security: Only executes commands that start with "gh {ALLOWED_COMMAND}"
@@ -249,8 +172,9 @@ async function executeGitHubCommand(command, args = [], options = {}) {
249
172
  if (!isValidGhCommand(command)) {
250
173
  return createErrorResult('Command not registered', new Error(`GitHub command '${command}' is not in the allowed list`));
251
174
  }
252
- // Build command with validated prefix - no sanitization needed since we control the prefix
253
- const fullCommand = `gh ${command} ${args.join(' ')}`;
175
+ // Build command with validated prefix and properly escaped arguments
176
+ const escapedArgs = args.map(escapeShellArg);
177
+ const fullCommand = `gh ${command} ${escapedArgs.join(' ')}`;
254
178
  const executeGhCommand = () => executeCommand(fullCommand, 'github', options);
255
179
  if (options.cache) {
256
180
  const cacheKey = generateCacheKey('gh-exec', { command, args });
@@ -307,10 +231,13 @@ async function executeCommand(fullCommand, type, options = {}) {
307
231
  }
308
232
  }
309
233
 
234
+ const TOOL_NAME$9 = 'api_status_check';
235
+ const DESCRIPTION$9 = `Gets the list of user github organizations (in case the tool needs to use them in "owner" fields in github search) and checks users gh cli and npm cli login status.
236
+ Use when user asks on specific implementaon in his organization (e.g. - I work at Wix, or search Wix code about X) or when cli is failing.`;
310
237
  function registerApiStatusCheckTool(server) {
311
- server.tool(TOOL_NAMES.API_STATUS_CHECK, TOOL_DESCRIPTIONS[TOOL_NAMES.API_STATUS_CHECK], {}, {
312
- title: 'Verify Tools Readiness and Authentication',
313
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.API_STATUS_CHECK],
238
+ server.tool(TOOL_NAME$9, DESCRIPTION$9, {}, {
239
+ title: 'Check API Connections and Github Organizations',
240
+ description: DESCRIPTION$9,
314
241
  readOnlyHint: true,
315
242
  destructiveHint: false,
316
243
  idempotentHint: true,
@@ -426,64 +353,72 @@ function registerApiStatusCheckTool(server) {
426
353
  });
427
354
  }
428
355
  catch (error) {
429
- return createResult(`API status check failed: ${error.message}`, true);
356
+ return createResult('API status check failed - verify GitHub CLI and NPM are installed and accessible', true);
430
357
  }
431
358
  });
432
359
  }
433
360
 
434
- /**
435
- * Registers the GitHub Code Search tool with the MCP server.
436
- *
437
- * This tool provides semantic code search across GitHub repositories using the GitHub CLI.
438
- * It supports advanced search features like boolean operators, qualifiers, and filters.
439
- *
440
- * Key features:
441
- * - Boolean operator support (AND, OR, NOT) in queries
442
- * - GitHub qualifier support (language:, path:, etc.)
443
- * - Repository, owner, and organization filtering
444
- * - File type, size, and visibility filtering
445
- * - Multiple match scopes and filtering options
446
- */
361
+ const TOOL_NAME$8 = 'github_search_code';
362
+ const DESCRIPTION$8 = `Search code across GitHub repositories using strategic boolean operators and filters usign "gh code search" command.
363
+
364
+ STRATEGIC SEARCH PATTERNS:
365
+
366
+ OR LOGIC (Exploratory Discovery):
367
+ Auto-applied to multi-word queries: "useState hook" → "useState OR hook"
368
+ Best for: Learning, finding alternatives, casting wide nets
369
+ Scope: BROADEST - finds files with ANY of the terms
370
+
371
+ AND LOGIC (Precise Intersection):
372
+ Explicit requirement: "react AND hooks" requires BOTH terms present
373
+ Best for: Finding specific combinations, technology intersections
374
+ • Scope: RESTRICTIVE - only files containing ALL terms
375
+
376
+ EXACT PHRASE (Laser Targeting):
377
+ • Escaped quotes: "useState hook" finds literal "useState hook" sequence
378
+ • Best for: Documentation titles, specific API calls, exact implementations
379
+ • Scope: MOST PRECISE - only exact sequence matches
380
+
381
+ NOT LOGIC (Noise Filtering):
382
+ • Exclude unwanted results: "authentication NOT test NOT mock"
383
+ • Best for: Removing examples, tests, deprecated code
384
+
385
+ RESTRICTIVENESS SCALE: OR < AND < Exact Phrase (Broadest → Most Precise)
386
+
387
+ COMBINE FILTERS: Mix query with language, owner, path filters for laser-focused results.`;
447
388
  function registerGitHubSearchCodeTool(server) {
448
- server.tool(TOOL_NAMES.GITHUB_SEARCH_CODE, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_CODE], {
389
+ server.tool(TOOL_NAME$8, DESCRIPTION$8, {
449
390
  query: z
450
391
  .string()
451
392
  .min(1)
452
- .describe('Search query with boolean operators (AND, OR, NOT) and qualifiers. ' +
453
- 'Examples: "react lifecycle", "error handling", "logger AND debug", "config OR settings", "main NOT test". ' +
454
- 'Use quotes for exact phrases. Supports GitHub search syntax.'),
393
+ .describe('Search query with strategic boolean operators. SEARCH PATTERNS: OR (auto-default): "useState hook" → "useState OR hook" for BROADEST discovery. AND (explicit): "react AND hooks" requires BOTH terms for RESTRICTIVE intersection. EXACT PHRASE (escaped quotes): "useState hook" finds literal sequence for MOST PRECISE targeting. NOT (filtering): "auth NOT test" excludes unwanted results. USAGE GUIDE: Use OR for exploration/alternatives, AND for specific combinations, exact phrases for documentation/APIs, NOT for removing noise. RESTRICTIVENESS: OR < AND < Exact Phrase. No parentheses - simple boolean logic only.'),
455
394
  owner: z
456
395
  .union([z.string(), z.array(z.string())])
457
396
  .optional()
458
- .describe('Repository owner/organization(s). Single string or array for multiple owners. Leave empty for global search.'),
397
+ .describe('Repository owner/organization filter. Examples: "microsoft", "google". Combines with other filters for targeted search. get from user orgamizations in case of private repositories search (e.g. for employees of organizations)'),
459
398
  repo: z
460
- .array(z.string())
399
+ .union([z.string(), z.array(z.string())])
461
400
  .optional()
462
- .describe('Specific repositories in "owner/repo" format. Requires owner parameter to be set.'),
401
+ .describe('Specific repositories in "owner/repo" format. Examples: "facebook/react", "microsoft/vscode". Requires owner parameter.'),
463
402
  language: z
464
403
  .string()
465
404
  .optional()
466
- .describe('Programming language filter (e.g., "javascript", "python", "go").'),
405
+ .describe('Programming language filter. Examples: "javascript", "python", "typescript", "go". Highly effective for targeted searches.'),
467
406
  extension: z
468
407
  .string()
469
408
  .optional()
470
- .describe('File extension filter without dot (e.g., "js", "py", "md").'),
409
+ .describe('File extension filter without dot. Examples: "js", "ts", "py", "md", "json". Precise file type targeting.'),
471
410
  filename: z
472
411
  .string()
473
412
  .optional()
474
- .describe('Exact filename filter (e.g., "package.json", "Dockerfile", "README.md").'),
413
+ .describe('Exact filename filter. Examples: "package.json", "Dockerfile", "README.md", "index.js". Perfect for config files.'),
475
414
  path: z
476
415
  .string()
477
416
  .optional()
478
- .describe('Directory path filter with wildcards (e.g., "src/", "*/tests/*", "docs/**").'),
417
+ .describe('Directory path filter. Examples: "src/", "test/", "docs/", "components/". Focus search on specific directories.'),
479
418
  size: z
480
419
  .string()
481
420
  .optional()
482
421
  .describe('File size filter in KB with operators (e.g., ">100", "<50", "10..100").'),
483
- visibility: z
484
- .enum(['public', 'private', 'internal'])
485
- .optional()
486
- .describe('Repository visibility filter. "public" for public repos, "private" for private repos you have access to.'),
487
422
  limit: z
488
423
  .number()
489
424
  .int()
@@ -495,11 +430,14 @@ function registerGitHubSearchCodeTool(server) {
495
430
  match: z
496
431
  .union([z.enum(['file', 'path']), z.array(z.enum(['file', 'path']))])
497
432
  .optional()
498
- .describe('Search scope: "file" for content search, "path" for filename/path search. If array provided, only first value is used due to GitHub API limitations.'),
433
+ .describe('Search scope: "file" searches code content, "path" searches filenames/paths. Use "path" to find files by name.'),
434
+ visibility: z
435
+ .enum(['public', 'private', 'internal'])
436
+ .optional()
437
+ .describe('Repository visibility filter: "public", "private", or "internal". Defaults to accessible repositories.'),
499
438
  }, {
500
- title: 'Search Code in GitHub Repositories',
501
- description: 'Search code across GitHub with boolean operators (AND, OR, NOT), qualifiers, and filters. ' +
502
- 'Supports language, file type, owner, repository, path, and visibility filtering.',
439
+ title: TOOL_NAME$8,
440
+ description: DESCRIPTION$8,
503
441
  readOnlyHint: true,
504
442
  destructiveHint: false,
505
443
  idempotentHint: true,
@@ -507,7 +445,7 @@ function registerGitHubSearchCodeTool(server) {
507
445
  }, async (args) => {
508
446
  try {
509
447
  if (args.repo && !args.owner) {
510
- return createResult('Repository search requires owner parameter', true);
448
+ return createResult('Repository search requires owner parameter - specify owner when searching specific repositories', true);
511
449
  }
512
450
  const result = await searchGitHubCode(args);
513
451
  if (result.isError) {
@@ -519,114 +457,206 @@ function registerGitHubSearchCodeTool(server) {
519
457
  const items = Array.isArray(codeResults) ? codeResults : [];
520
458
  return createSuccessResult$1({
521
459
  query: args.query,
460
+ processed_query: parseSearchQuery(args.query, args),
522
461
  total_count: items.length,
523
462
  items: items,
463
+ cli_command: execResult.command,
464
+ debug_info: {
465
+ has_complex_boolean_logic: hasComplexBooleanLogic(args.query),
466
+ escaped_args: buildGitHubCliArgs(args),
467
+ original_query: args.query,
468
+ },
524
469
  });
525
470
  }
526
471
  catch (error) {
527
- return createErrorResult$1('Code search failed', error);
472
+ return createErrorResult$1('GitHub code search failed - check repository access or simplify query', error);
473
+ }
474
+ });
475
+ }
476
+ /**
477
+ * Enhanced query parser that handles exact strings, boolean operators, and filters
478
+ */
479
+ function parseSearchQuery(query, filters) {
480
+ // Step 1: Handle quoted strings more intelligently
481
+ // Convert escaped quotes to simple quotes to avoid shell escaping issues
482
+ let processedQuery = query.replace(/\\"/g, '"');
483
+ // Step 2: Preserve exact phrases (quoted strings)
484
+ const exactPhrases = [];
485
+ // Extract quoted strings and replace with placeholders
486
+ const quotedMatches = processedQuery.match(/"[^"]+"/g) || [];
487
+ quotedMatches.forEach((match, index) => {
488
+ const placeholder = `__EXACT_PHRASE_${index}__`;
489
+ exactPhrases.push(match);
490
+ processedQuery = processedQuery.replace(match, placeholder);
491
+ });
492
+ // Step 3: Smart boolean logic - default to OR between terms if no explicit operators
493
+ let searchQuery = processedQuery;
494
+ // Check if query already has explicit boolean operators
495
+ if (!hasComplexBooleanLogic(processedQuery)) {
496
+ // Split by whitespace and join with OR for better search results
497
+ const terms = processedQuery
498
+ .trim()
499
+ .split(/\s+/)
500
+ .filter(term => term.length > 0);
501
+ if (terms.length > 1) {
502
+ searchQuery = terms.join(' OR ');
503
+ }
504
+ }
505
+ // Step 4: Add GitHub-specific filters that go in the query string
506
+ const githubFilters = [];
507
+ if (filters.path) {
508
+ githubFilters.push(`path:${filters.path}`);
509
+ }
510
+ if (filters.visibility) {
511
+ githubFilters.push(`visibility:${filters.visibility}`);
512
+ }
513
+ // For complex boolean queries, add language/extension/filename/size to query string
514
+ const hasComplexLogic = hasComplexBooleanLogic(searchQuery);
515
+ if (hasComplexLogic) {
516
+ if (filters.language) {
517
+ githubFilters.push(`language:${filters.language}`);
518
+ }
519
+ if (filters.extension) {
520
+ githubFilters.push(`extension:${filters.extension}`);
521
+ }
522
+ if (filters.filename) {
523
+ githubFilters.push(`filename:${filters.filename}`);
528
524
  }
525
+ if (filters.size) {
526
+ githubFilters.push(`size:${filters.size}`);
527
+ }
528
+ }
529
+ // Step 5: Combine query with GitHub filters
530
+ if (githubFilters.length > 0) {
531
+ searchQuery = `${searchQuery} ${githubFilters.join(' ')}`;
532
+ }
533
+ // Step 6: Restore exact phrases
534
+ exactPhrases.forEach((phrase, index) => {
535
+ const placeholder = `__EXACT_PHRASE_${index}__`;
536
+ searchQuery = searchQuery.replace(placeholder, phrase);
529
537
  });
538
+ return searchQuery.trim();
539
+ }
540
+ /**
541
+ * Check if query contains complex boolean logic that might conflict with CLI flags
542
+ */
543
+ function hasComplexBooleanLogic(query) {
544
+ const booleanOperators = /\b(AND|OR|NOT)\b/i;
545
+ return booleanOperators.test(query);
546
+ }
547
+ /**
548
+ * Build command line arguments for GitHub CLI
549
+ */
550
+ function buildGitHubCliArgs(params) {
551
+ const args = ['code'];
552
+ // Parse and add the main search query
553
+ const searchQuery = parseSearchQuery(params.query, params);
554
+ args.push(searchQuery);
555
+ // Add CLI flags - Always add basic flags, but be careful with complex boolean queries
556
+ const hasComplexLogic = hasComplexBooleanLogic(searchQuery);
557
+ // For complex boolean queries, add filters to the query string instead of CLI flags
558
+ if (hasComplexLogic) ;
559
+ else {
560
+ // Simple queries: use CLI flags for better performance
561
+ if (params.language) {
562
+ args.push(`--language=${params.language}`);
563
+ }
564
+ if (params.extension) {
565
+ args.push(`--extension=${params.extension}`);
566
+ }
567
+ if (params.filename) {
568
+ args.push(`--filename=${params.filename}`);
569
+ }
570
+ if (params.size) {
571
+ args.push(`--size=${params.size}`);
572
+ }
573
+ }
574
+ if (params.limit) {
575
+ args.push(`--limit=${params.limit}`);
576
+ }
577
+ // Handle match parameter - can be string or array
578
+ if (params.match) {
579
+ const matchValues = Array.isArray(params.match)
580
+ ? params.match
581
+ : [params.match];
582
+ // GitHub API limitation: can't use both in:file and in:path in same query
583
+ // Use the first match type when multiple are provided
584
+ const matchValue = matchValues[0];
585
+ args.push(`--match=${matchValue}`);
586
+ }
587
+ // Handle owner parameter - can be string or array
588
+ if (params.owner && !params.repo) {
589
+ const ownerValues = Array.isArray(params.owner)
590
+ ? params.owner
591
+ : [params.owner];
592
+ ownerValues.forEach(owner => args.push(`--owner=${owner}`));
593
+ }
594
+ // Handle repository filters
595
+ if (params.owner && params.repo) {
596
+ const owners = Array.isArray(params.owner) ? params.owner : [params.owner];
597
+ const repos = Array.isArray(params.repo) ? params.repo : [params.repo];
598
+ // Create repo filters for each owner/repo combination
599
+ owners.forEach(owner => {
600
+ repos.forEach(repo => {
601
+ // Handle both "owner/repo" format and just "repo" format
602
+ if (repo.includes('/')) {
603
+ args.push(`--repo=${repo}`);
604
+ }
605
+ else {
606
+ args.push(`--repo=${owner}/${repo}`);
607
+ }
608
+ });
609
+ });
610
+ }
611
+ // JSON output with all available fields
612
+ args.push('--json=repository,path,textMatches,sha,url');
613
+ return args;
530
614
  }
531
615
  async function searchGitHubCode(params) {
532
616
  const cacheKey = generateCacheKey('gh-code', params);
533
617
  return withCache(cacheKey, async () => {
534
618
  try {
535
- const args = ['code'];
536
- // Build the main query - preserve boolean operators and GitHub syntax
537
- let query = params.query;
538
- // Add path filter to query if provided (GitHub search syntax)
539
- if (params.path) {
540
- query = `${query} path:${params.path}`;
541
- }
542
- // Add visibility filter to query if provided
543
- if (params.visibility) {
544
- query = `${query} visibility:${params.visibility}`;
545
- }
546
- args.push(query);
547
- // Add command-line filters
548
- if (params.language)
549
- args.push(`--language=${params.language}`);
550
- if (params.extension)
551
- args.push(`--extension=${params.extension}`);
552
- if (params.filename)
553
- args.push(`--filename=${params.filename}`);
554
- if (params.size)
555
- args.push(`--size=${params.size}`);
556
- if (params.limit)
557
- args.push(`--limit=${params.limit}`);
558
- // Handle match parameter - can be string or array
559
- // Note: GitHub search API doesn't support multiple match types in a single query
560
- if (params.match) {
561
- const matchValues = Array.isArray(params.match)
562
- ? params.match
563
- : [params.match];
564
- // GitHub API limitation: can't use both in:file and in:path in same query
565
- // Use the first match type when multiple are provided
566
- const matchValue = matchValues[0];
567
- args.push(`--match=${matchValue}`);
568
- }
569
- // Handle owner parameter - can be string or array
570
- if (params.owner && !params.repo) {
571
- const ownerValues = Array.isArray(params.owner)
572
- ? params.owner
573
- : [params.owner];
574
- ownerValues.forEach(owner => args.push(`--owner=${owner}`));
575
- }
576
- // Handle repository filters
577
- if (params.owner && params.repo) {
578
- const owners = Array.isArray(params.owner)
579
- ? params.owner
580
- : [params.owner];
581
- const repos = params.repo;
582
- // Create repo filters for each owner/repo combination
583
- owners.forEach(owner => {
584
- repos.forEach(repo => {
585
- // Handle both "owner/repo" format and just "repo" format
586
- if (repo.includes('/')) {
587
- args.push(`--repo=${repo}`);
588
- }
589
- else {
590
- args.push(`--repo=${owner}/${repo}`);
591
- }
592
- });
593
- });
594
- }
595
- // JSON output with all available fields
596
- args.push('--json=repository,path,textMatches,sha,url');
619
+ const args = buildGitHubCliArgs(params);
597
620
  const result = await executeGitHubCommand('search', args, {
598
621
  cache: false,
599
622
  });
600
623
  return result;
601
624
  }
602
625
  catch (error) {
603
- return createErrorResult$1('Failed to execute search command', error);
626
+ return createErrorResult$1('Code search command failed - verify GitHub CLI is authenticated', error);
604
627
  }
605
628
  });
606
629
  }
607
630
 
631
+ const TOOL_NAME$7 = 'github_get_file_content';
632
+ const DESCRIPTION$7 = `Read file content. This tool REQUIRES exact path verification from github_get_contents or package view exports. If fetching fails, re-check file existence with github_get_contents or branch name.`;
608
633
  function registerFetchGitHubFileContentTool(server) {
609
- server.tool(TOOL_NAMES.GITHUB_GET_FILE_CONTENT, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_GET_FILE_CONTENT], {
634
+ server.tool(TOOL_NAME$7, DESCRIPTION$7, {
610
635
  owner: z
611
636
  .string()
612
637
  .min(1)
638
+ .max(100)
639
+ .regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/)
613
640
  .describe(`Repository owner/organization (e.g., 'microsoft', 'facebook')`),
614
641
  repo: z
615
642
  .string()
616
643
  .min(1)
644
+ .max(100)
645
+ .regex(/^[a-zA-Z0-9._-]+$/)
617
646
  .describe(`Repository name (e.g., 'vscode', 'react'). Case-sensitive.`),
618
647
  branch: z
619
648
  .string()
620
649
  .min(1)
650
+ .max(255)
651
+ .regex(/^[^\s]+$/)
621
652
  .describe(`Branch name (e.g., 'main', 'master'). Auto-fallback to common branches if not found.`),
622
653
  filePath: z
623
654
  .string()
624
655
  .min(1)
625
656
  .describe(`File path from repository root (e.g., 'README.md', 'src/index.js'). Use github_get_contents to explore structure.`),
626
657
  }, {
627
- title: 'Read File Content from GitHub Repositories',
628
- description: `Fetches complete file content from GitHub repositories. ` +
629
- `Handles text files up to 500KB, detects binary files, supports branch fallback.`,
658
+ title: TOOL_NAME$7,
659
+ description: DESCRIPTION$7,
630
660
  readOnlyHint: true,
631
661
  destructiveHint: false,
632
662
  idempotentHint: true,
@@ -661,26 +691,7 @@ function registerFetchGitHubFileContentTool(server) {
661
691
  return result;
662
692
  }
663
693
  catch (error) {
664
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
665
- let suggestions = [];
666
- if (errorMessage.includes('404') ||
667
- errorMessage.includes('Not Found')) {
668
- suggestions = [
669
- TOOL_NAMES.GITHUB_GET_CONTENTS,
670
- TOOL_NAMES.GITHUB_SEARCH_CODE,
671
- ];
672
- }
673
- else if (errorMessage.includes('403') ||
674
- errorMessage.includes('Forbidden')) {
675
- suggestions = [TOOL_NAMES.API_STATUS_CHECK];
676
- }
677
- else if (errorMessage.includes('branch')) {
678
- suggestions = [TOOL_NAMES.GITHUB_GET_CONTENTS];
679
- }
680
- else {
681
- suggestions = getErrorSuggestions(TOOL_NAMES.GITHUB_GET_FILE_CONTENT);
682
- }
683
- return createResult(`Failed to fetch file content: ${errorMessage}. Context: ${args.owner}/${args.repo}/${args.filePath} on ${args.branch}`, true, suggestions);
694
+ return createResult('File fetch failed - verify file path exists or try github_get_contents first', true);
684
695
  }
685
696
  });
686
697
  }
@@ -713,19 +724,19 @@ async function fetchGitHubFileContent(params) {
713
724
  }
714
725
  // Handle common errors
715
726
  if (errorMsg.includes('404')) {
716
- return createErrorResult$1(`File not found: ${filePath}`, new Error(`File does not exist in ${owner}/${repo} on branch ${branch}`));
727
+ return createErrorResult$1('File not found - verify path with github_get_contents first', new Error(filePath));
717
728
  }
718
729
  else if (errorMsg.includes('403')) {
719
- return createErrorResult$1(`Access denied: ${filePath}`, new Error(`Permission denied for ${owner}/${repo}`));
730
+ return createErrorResult$1('Access denied - repository may be private or require authentication', new Error(`${owner}/${repo}`));
720
731
  }
721
732
  else {
722
- return createErrorResult$1(`Failed to fetch file: ${filePath}`, new Error(errorMsg));
733
+ return createErrorResult$1('Fetch failed - check repository and file path', new Error(errorMsg));
723
734
  }
724
735
  }
725
736
  return await processFileContent(result, owner, repo, branch, filePath);
726
737
  }
727
738
  catch (error) {
728
- return createErrorResult$1(`Unexpected error fetching file: ${filePath}`, error);
739
+ return createErrorResult$1('Unexpected error during file fetch - check connection and permissions', error);
729
740
  }
730
741
  });
731
742
  }
@@ -735,30 +746,30 @@ async function processFileContent(result, owner, repo, branch, filePath) {
735
746
  const fileData = JSON.parse(execResult.result);
736
747
  // Check if it's a directory
737
748
  if (Array.isArray(fileData)) {
738
- return createErrorResult$1(`Path is a directory: ${filePath}`, new Error(`"${filePath}" is a directory, not a file`));
749
+ return createErrorResult$1('Path is directory - use github_get_contents to browse directory structure', new Error(filePath));
739
750
  }
740
751
  const fileSize = fileData.size || 0;
741
752
  const MAX_FILE_SIZE = 500 * 1024; // 500KB limit for simplicity
742
753
  // Check file size
743
754
  if (fileSize > MAX_FILE_SIZE) {
744
- return createErrorResult$1(`File too large: ${filePath}`, new Error(`File size (${Math.round(fileSize / 1024)}KB) exceeds limit (500KB)`));
755
+ return createErrorResult$1('File too large - files over 500KB cannot be fetched', new Error(`${Math.round(fileSize / 1024)}KB > 500KB`));
745
756
  }
746
757
  // Get and decode content
747
758
  const base64Content = fileData.content?.replace(/\s/g, ''); // Remove all whitespace
748
759
  if (!base64Content) {
749
- return createErrorResult$1(`Empty file: ${filePath}`, new Error(`File appears to be empty`));
760
+ return createErrorResult$1('Empty file - file has no content to display', new Error(filePath));
750
761
  }
751
762
  let decodedContent;
752
763
  try {
753
764
  const buffer = Buffer.from(base64Content, 'base64');
754
765
  // Simple binary check - look for null bytes
755
766
  if (buffer.indexOf(0) !== -1) {
756
- return createErrorResult$1(`Binary file detected: ${filePath}`, new Error(`Binary files cannot be displayed as text`));
767
+ return createErrorResult$1('Binary file detected - cannot display binary content as text', new Error(filePath));
757
768
  }
758
769
  decodedContent = buffer.toString('utf-8');
759
770
  }
760
771
  catch (decodeError) {
761
- return createErrorResult$1(`Failed to decode file: ${filePath}`, new Error(`Unable to decode file as UTF-8`));
772
+ return createErrorResult$1('Decode failed - file encoding not supported or corrupted', new Error(filePath));
762
773
  }
763
774
  // Return simplified response
764
775
  const response = {
@@ -776,8 +787,17 @@ async function processFileContent(result, owner, repo, branch, filePath) {
776
787
  return createSuccessResult$1(response);
777
788
  }
778
789
 
790
+ const TOOL_NAME$6 = 'github_search_repositories';
791
+ const DESCRIPTION$6 = `Search repositories by name/description. Start shallow and go broad: use topics for exploratory discovery (e.g., topic:["cli","typescript","api"]) to find ecosystem patterns.
792
+ PRIMARY FILTERS work alone: owner, language, stars, topic, forks. SECONDARY FILTERS require a query or primary filter: license, created, archived, includeForks, updated, visibility, match.
793
+ SMART REPOS SEARCH PATTERNS: Use topic:["cli","typescript"] for semantic discovery; stars:">100" for quality; owner:"microsoft" for organization repos. Query supports GitHub syntax: "language:Go OR language:Rust".
794
+
795
+ EFFICIENCY NOTE: If you have a package name, use npm_view_package FIRST to get repositoryGitUrl - this tool becomes UNNECESSARY
796
+
797
+ SMART INTEGRATION: When finding packages → npm_view_package + npm_package_search provide direct repo access
798
+ AVOID: Searching for "react" repos when npm_view_package("react") gives you the exact repository instantly`;
779
799
  function registerSearchGitHubReposTool(server) {
780
- server.tool(TOOL_NAMES.GITHUB_SEARCH_REPOS, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_REPOS], {
800
+ server.tool(TOOL_NAME$6, DESCRIPTION$6, {
781
801
  query: z
782
802
  .string()
783
803
  .optional()
@@ -786,52 +806,66 @@ function registerSearchGitHubReposTool(server) {
786
806
  owner: z
787
807
  .string()
788
808
  .optional()
789
- .describe('Repository owner/organization. PRIMARY FILTER - works alone.'),
809
+ .describe('Repository owner/organization (e.g., "microsoft", "facebook").'),
790
810
  language: z
791
811
  .string()
792
812
  .optional()
793
- .describe('Programming language. PRIMARY FILTER - works alone.'),
813
+ .describe('Programming language (e.g., "javascript", "python", "go").'),
794
814
  stars: z
795
815
  .string()
796
816
  .optional()
797
- .describe('Stars count with ranges: "100", ">500", "<50", "10..100", ">=1000". PRIMARY FILTER - works alone. Use >100 for quality projects.'),
817
+ .describe('Stars count with ranges: "100", ">500", "<50", "10..100", ">=1000". Use >100 for quality projects.'),
798
818
  topic: z
799
819
  .array(z.string())
800
820
  .optional()
801
- .describe('Filter by topics. PRIMARY FILTER - works alone.'),
802
- forks: z
821
+ .describe('Filter by topics (e.g., ["cli", "typescript", "api"]).'),
822
+ forks: z.number().optional().describe('Exact forks count.'),
823
+ numberOfTopics: z
803
824
  .number()
804
825
  .optional()
805
- .describe('Exact forks count. PRIMARY FILTER - works alone.'),
826
+ .describe('Filter on number of topics.'),
806
827
  // SECONDARY FILTERS (require query or primary filter)
807
828
  license: z
808
829
  .array(z.string())
809
830
  .optional()
810
- .describe('License types. REQUIRES query or primary filter.'),
831
+ .describe('License types (e.g., ["mit", "apache-2.0"]).'),
811
832
  match: z
812
833
  .enum(['name', 'description', 'readme'])
813
834
  .optional()
814
- .describe('Search scope. REQUIRES query.'),
835
+ .describe('Search scope: "name", "description", or "readme".'),
815
836
  visibility: z
816
837
  .enum(['public', 'private', 'internal'])
817
838
  .optional()
818
- .describe('Repository visibility. REQUIRES query or primary filter.'),
839
+ .describe('Repository visibility filter.'),
819
840
  created: z
820
841
  .string()
821
842
  .optional()
822
- .describe('Created date filter: ">2020-01-01", "<2023-12-31". REQUIRES query or primary filter.'),
843
+ .describe('Created date filter: ">2020-01-01", "<2023-12-31", "2022-01-01..2023-12-31".'),
823
844
  updated: z
824
845
  .string()
825
846
  .optional()
826
- .describe('Updated date filter. REQUIRES query or primary filter.'),
827
- archived: z
828
- .boolean()
829
- .optional()
830
- .describe('Archived state. REQUIRES query or primary filter.'),
847
+ .describe('Updated date filter (same format as created).'),
848
+ archived: z.boolean().optional().describe('Filter by archived state.'),
831
849
  includeForks: z
832
850
  .enum(['false', 'true', 'only'])
833
851
  .optional()
834
- .describe('Include forks. REQUIRES query or primary filter.'),
852
+ .describe('Include forks: "false" (default), "true", or "only".'),
853
+ goodFirstIssues: z
854
+ .string()
855
+ .optional()
856
+ .describe('Filter by good first issues count (e.g., ">=10", ">5").'),
857
+ helpWantedIssues: z
858
+ .string()
859
+ .optional()
860
+ .describe('Filter by help wanted issues count (e.g., ">=5", ">10").'),
861
+ followers: z
862
+ .number()
863
+ .optional()
864
+ .describe('Filter by number of followers.'),
865
+ size: z
866
+ .string()
867
+ .optional()
868
+ .describe('Repository size filter in KB (e.g., ">100", "<50", "10..100").'),
835
869
  // Sorting and limits
836
870
  sort: z
837
871
  .enum(['forks', 'help-wanted-issues', 'stars', 'updated', 'best-match'])
@@ -852,8 +886,8 @@ function registerSearchGitHubReposTool(server) {
852
886
  .default(25)
853
887
  .describe('Maximum results (default: 25, max: 50)'),
854
888
  }, {
855
- title: 'GitHub Repository Search',
856
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_REPOS],
889
+ title: TOOL_NAME$6,
890
+ description: DESCRIPTION$6,
857
891
  readOnlyHint: true,
858
892
  destructiveHint: false,
859
893
  idempotentHint: true,
@@ -875,7 +909,7 @@ function registerSearchGitHubReposTool(server) {
875
909
  return result;
876
910
  }
877
911
  catch (error) {
878
- return createResult(`Search failed: ${error.message}`, true, getErrorSuggestions(TOOL_NAMES.GITHUB_SEARCH_REPOS));
912
+ return createResult('Repository search failed - check query syntax, filters, or try broader terms', true);
879
913
  }
880
914
  });
881
915
  }
@@ -942,7 +976,7 @@ async function searchGitHubRepos(params) {
942
976
  isPrivate: repo.isPrivate || false,
943
977
  isArchived: repo.isArchived || false,
944
978
  isFork: repo.isFork || false,
945
- topics: [], // Topics not available via CLI JSON output
979
+ topics: [], // GitHub CLI search repos doesn't provide topics in JSON output
946
980
  license: repo.license?.name || null,
947
981
  hasIssues: repo.hasIssues || false,
948
982
  openIssuesCount: repo.openIssuesCount || 0,
@@ -966,17 +1000,11 @@ async function searchGitHubRepos(params) {
966
1000
  }
967
1001
  : {
968
1002
  repositories: [],
969
- suggestions: [
970
- `${TOOL_NAMES.NPM_PACKAGE_SEARCH} "${params.query || 'package'}"`,
971
- `${TOOL_NAMES.GITHUB_SEARCH_CODE} "${params.query || 'code'}"`,
972
- 'Try broader search terms',
973
- 'Check spelling and try synonyms',
974
- ],
975
1003
  }),
976
1004
  });
977
1005
  }
978
1006
  catch (error) {
979
- return createErrorResult$1('Failed to search GitHub repositories', error);
1007
+ return createErrorResult$1('GitHub repository search failed - verify connection or try simpler query', error);
980
1008
  }
981
1009
  });
982
1010
  }
@@ -1009,12 +1037,22 @@ function buildGitHubReposSearchCommand(params) {
1009
1037
  });
1010
1038
  }
1011
1039
  else {
1012
- // For simple queries, use quoting logic
1013
- const queryString = needsQuoting(query) ? `"${query}"` : query;
1014
- args.push(queryString);
1040
+ // For simple queries, split by spaces to match GitHub CLI examples
1041
+ // "cli shell" becomes separate args: cli shell
1042
+ const queryParts = query.split(/\s+/).filter(part => part.length > 0);
1043
+ queryParts.forEach(part => {
1044
+ // Only quote if the part contains special characters
1045
+ if (needsQuoting(part)) {
1046
+ args.push(`"${part}"`);
1047
+ }
1048
+ else {
1049
+ args.push(part);
1050
+ }
1051
+ });
1015
1052
  }
1016
1053
  }
1017
1054
  // Add JSON output with specific fields for structured data parsing
1055
+ // Note: 'topics' field is not available in GitHub CLI search repos JSON output
1018
1056
  args.push('--json', 'name,fullName,description,language,stargazersCount,forksCount,updatedAt,createdAt,url,owner,isPrivate,license,hasIssues,openIssuesCount,isArchived,isFork,visibility');
1019
1057
  // PRIMARY FILTERS - Handle owner as single string (BaseSearchParams) or array
1020
1058
  if (params.owner) {
@@ -1029,6 +1067,8 @@ function buildGitHubReposSearchCommand(params) {
1029
1067
  args.push(`--forks=${params.forks}`);
1030
1068
  if (params.topic && params.topic.length > 0)
1031
1069
  args.push(`--topic=${params.topic.join(',')}`);
1070
+ if (params.numberOfTopics !== undefined)
1071
+ args.push(`--number-topics=${params.numberOfTopics}`);
1032
1072
  // Only add stars filter if it's a valid numeric value or range
1033
1073
  if (params.stars !== undefined &&
1034
1074
  params.stars !== '*' &&
@@ -1037,7 +1077,7 @@ function buildGitHubReposSearchCommand(params) {
1037
1077
  const starsValue = params.stars.trim();
1038
1078
  const isValidStars = /^(\d+|>\d+|<\d+|\d+\.\.\d+|>=\d+|<=\d+)$/.test(starsValue);
1039
1079
  if (isValidStars) {
1040
- args.push(`--stars="${params.stars}"`);
1080
+ args.push(`--stars=${params.stars}`);
1041
1081
  }
1042
1082
  }
1043
1083
  // SECONDARY FILTERS - only add if we have primary filters
@@ -1055,6 +1095,14 @@ function buildGitHubReposSearchCommand(params) {
1055
1095
  args.push(`--updated="${params.updated}"`);
1056
1096
  if (params.visibility)
1057
1097
  args.push(`--visibility=${params.visibility}`);
1098
+ if (params.goodFirstIssues)
1099
+ args.push(`--good-first-issues=${params.goodFirstIssues}`);
1100
+ if (params.helpWantedIssues)
1101
+ args.push(`--help-wanted-issues=${params.helpWantedIssues}`);
1102
+ if (params.followers !== undefined)
1103
+ args.push(`--followers=${params.followers}`);
1104
+ if (params.size)
1105
+ args.push(`--size=${params.size}`);
1058
1106
  // SORTING AND LIMITS
1059
1107
  if (params.limit)
1060
1108
  args.push(`--limit=${params.limit}`);
@@ -1068,12 +1116,14 @@ function buildGitHubReposSearchCommand(params) {
1068
1116
  return { command: 'search', args };
1069
1117
  }
1070
1118
 
1119
+ const TOOL_NAME$5 = 'github_search_commits';
1120
+ const DESCRIPTION$5 = `Search commit history with powerful boolean logic and exact phrase matching. Use advanced GitHub search syntax including AND/OR operators for precise commit discovery. Understand code evolution patterns, track bug fixes, and analyze development workflows over time. Filter by author, date ranges, commit content, and repository metadata with surgical precision.`;
1071
1121
  function registerSearchGitHubCommitsTool(server) {
1072
- server.tool(TOOL_NAMES.GITHUB_SEARCH_COMMITS, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_COMMITS], {
1122
+ server.tool(TOOL_NAME$5, DESCRIPTION$5, {
1073
1123
  query: z
1074
1124
  .string()
1075
1125
  .optional()
1076
- .describe('Search query with full GitHub syntax support: "readme typo" (AND), "bug fix" (phrase), "author:john OR committer:jane" (OR), "-- -author:botuser" (exclude). Optional - can search with just filters.'),
1126
+ .describe('Search query with POWERFUL boolean logic and exact phrase matching. BOOLEAN OPERATORS: "fix AND bug" (both required), "fix OR update" (either term), "readme typo" (implicit AND). EXACT PHRASES: "initial commit" (precise phrase matching). ADVANCED SYNTAX: "author:john OR committer:jane" (user qualifiers), "-- -author:botuser" (exclusions). STRENGTH: Surgical precision for commit discovery across millions of repositories. Optional - can search with just filters.'),
1077
1127
  // Basic filters
1078
1128
  owner: z
1079
1129
  .string()
@@ -1130,10 +1180,10 @@ function registerSearchGitHubCommitsTool(server) {
1130
1180
  .max(50)
1131
1181
  .optional()
1132
1182
  .default(25)
1133
- .describe('Maximum results (default: 25)'),
1183
+ .describe('Maximum results (default: 25, max: 50)'),
1134
1184
  }, {
1135
- title: 'GitHub Commit Search',
1136
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_COMMITS],
1185
+ title: TOOL_NAME$5,
1186
+ description: DESCRIPTION$5,
1137
1187
  readOnlyHint: true,
1138
1188
  destructiveHint: false,
1139
1189
  idempotentHint: true,
@@ -1152,7 +1202,7 @@ function registerSearchGitHubCommitsTool(server) {
1152
1202
  return result;
1153
1203
  }
1154
1204
  catch (error) {
1155
- return createResult(`Search failed: ${error.message}`, true, getErrorSuggestions(TOOL_NAMES.GITHUB_SEARCH_COMMITS));
1205
+ return createResult('Commit search failed - check query syntax, filters, or repository access', true);
1156
1206
  }
1157
1207
  });
1158
1208
  }
@@ -1247,17 +1297,10 @@ async function searchGitHubCommits(params) {
1247
1297
  query: params.query,
1248
1298
  total: 0,
1249
1299
  commits: [],
1250
- suggestions: [
1251
- `${TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS} "${params.query || 'pr'}"`,
1252
- `${TOOL_NAMES.GITHUB_SEARCH_ISSUES} "${params.query || 'issue'}"`,
1253
- `${TOOL_NAMES.GITHUB_SEARCH_CODE} "${params.query || 'code'}"`,
1254
- 'Try broader search terms',
1255
- 'Check spelling and try synonyms',
1256
- ],
1257
1300
  });
1258
1301
  }
1259
1302
  catch (error) {
1260
- return createErrorResult$1('Failed to search GitHub commits', error);
1303
+ return createErrorResult$1('GitHub commit search failed - verify repository exists or try different filters', error);
1261
1304
  }
1262
1305
  });
1263
1306
  }
@@ -1347,12 +1390,22 @@ function buildGitHubCommitsSearchCommand(params) {
1347
1390
  }
1348
1391
 
1349
1392
  // TODO: add PR commeents. e.g, gh pr view <PR_NUMBER_OR_URL_OR_BRANCH> --comments
1393
+ const TOOL_NAME$4 = 'github_search_pull_requests';
1394
+ const DESCRIPTION$4 = `Find pull requests and implementations with detailed metadata. Discover how features were implemented, code review patterns, and development workflows.
1395
+
1396
+ SEARCH PATTERNS SUPPORTED:
1397
+ • BOOLEAN OPERATORS: "fix AND bug" (both required), "refactor OR cleanup" (either term), "feature NOT draft" (excludes draft)
1398
+ • EXACT PHRASES: "initial commit" (precise phrase matching)
1399
+ • GITHUB QUALIFIERS: Built-in support for "is:merged", "review:approved", "base:main", etc.
1400
+ • COMBINABLE: Mix search terms with filters for targeted PR discovery
1401
+
1402
+ Filter by state, author, reviewer, or merge status for comprehensive development workflow analysis.`;
1350
1403
  function registerSearchGitHubPullRequestsTool(server) {
1351
- server.tool(TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS], {
1404
+ server.tool(TOOL_NAME$4, DESCRIPTION$4, {
1352
1405
  query: z
1353
1406
  .string()
1354
1407
  .min(1, 'Search query is required and cannot be empty')
1355
- .describe('Search query to find pull requests'),
1408
+ .describe('Search query with GITHUB SEARCH SYNTAX support. BOOLEAN OPERATORS: "fix AND bug" (both required), "refactor OR cleanup" (either term), "feature NOT draft" (excludes). EXACT PHRASES: "initial commit" (precise matching). GITHUB QUALIFIERS: "is:merged review:approved base:main" (native GitHub syntax). COMBINED: Mix boolean logic with qualifiers for precise PR discovery.'),
1356
1409
  owner: z.string().optional().describe('Repository owner/organization'),
1357
1410
  repo: z.string().optional().describe('Repository name'),
1358
1411
  author: z.string().optional().describe('Filter by pull request author'),
@@ -1377,6 +1430,15 @@ function registerSearchGitHubPullRequestsTool(server) {
1377
1430
  mergedAt: z.string().optional().describe('Filter by merged date'),
1378
1431
  closed: z.string().optional().describe('Filter by closed date'),
1379
1432
  draft: z.boolean().optional().describe('Filter by draft state'),
1433
+ checks: z
1434
+ .enum(['pending', 'success', 'failure'])
1435
+ .optional()
1436
+ .describe('Filter based on status of the checks'),
1437
+ merged: z.boolean().optional().describe('Filter based on merged state'),
1438
+ review: z
1439
+ .enum(['none', 'required', 'approved', 'changes_requested'])
1440
+ .optional()
1441
+ .describe('Filter based on review status'),
1380
1442
  limit: z
1381
1443
  .number()
1382
1444
  .int()
@@ -1384,7 +1446,7 @@ function registerSearchGitHubPullRequestsTool(server) {
1384
1446
  .max(50)
1385
1447
  .optional()
1386
1448
  .default(25)
1387
- .describe('Maximum results (default: 25)'),
1449
+ .describe('Maximum results (default: 25, max: 50)'),
1388
1450
  sort: z
1389
1451
  .enum([
1390
1452
  'comments',
@@ -1407,24 +1469,24 @@ function registerSearchGitHubPullRequestsTool(server) {
1407
1469
  .default('desc')
1408
1470
  .describe('Order (default: desc)'),
1409
1471
  }, {
1410
- title: 'Search Pull Requests for Implementation Analysis',
1411
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS],
1472
+ title: TOOL_NAME$4,
1473
+ description: DESCRIPTION$4,
1412
1474
  readOnlyHint: true,
1413
1475
  destructiveHint: false,
1414
1476
  idempotentHint: true,
1415
1477
  openWorldHint: true,
1416
1478
  }, async (args) => {
1417
1479
  if (!args.query?.trim()) {
1418
- return createErrorResult$1('Search query is required and cannot be empty', new Error('Invalid query'));
1480
+ return createErrorResult$1('Search query is required and cannot be empty - provide keywords to search for pull requests', new Error('Invalid query'));
1419
1481
  }
1420
1482
  if (args.query.length > 256) {
1421
- return createErrorResult$1('Search query is too long. Please limit to 256 characters or less.', new Error('Query too long'));
1483
+ return createErrorResult$1('Search query is too long. Please limit to 256 characters or less - simplify your search terms', new Error('Query too long'));
1422
1484
  }
1423
1485
  try {
1424
1486
  return await searchGitHubPullRequests(args);
1425
1487
  }
1426
1488
  catch (error) {
1427
- return createErrorResult$1('Failed to search GitHub pull requests', error);
1489
+ return createErrorResult$1('GitHub pull requests search failed - verify repository access and query syntax', error);
1428
1490
  }
1429
1491
  });
1430
1492
  }
@@ -1516,6 +1578,12 @@ function buildGitHubPullRequestsAPICommand(params) {
1516
1578
  queryParts.push(`merged:${params.mergedAt}`);
1517
1579
  if (params.draft !== undefined)
1518
1580
  queryParts.push(`draft:${params.draft}`);
1581
+ if (params.checks)
1582
+ queryParts.push(`status:${params.checks}`);
1583
+ if (params.merged !== undefined)
1584
+ queryParts.push(`is:${params.merged ? 'merged' : 'unmerged'}`);
1585
+ if (params.review)
1586
+ queryParts.push(`review:${params.review}`);
1519
1587
  // Add type qualifier to search only pull requests
1520
1588
  queryParts.push('type:pr');
1521
1589
  const query = queryParts.filter(Boolean).join(' ');
@@ -1528,13 +1596,22 @@ function buildGitHubPullRequestsAPICommand(params) {
1528
1596
  return { command: 'api', args: [apiPath] };
1529
1597
  }
1530
1598
 
1599
+ const TOOL_NAME$3 = 'npm_package_search';
1600
+ const DESCRIPTION$3 = `Search npm packages by keywords using fuzzy matching.
1601
+
1602
+ IMPORTANT LIMITATIONS:
1603
+ • NO BOOLEAN OPERATORS: NPM search does NOT support AND/OR/NOT - use space-separated keywords for broader search
1604
+ • FUZZY MATCHING ONLY: No exact phrase matching - searches are approximate keyword matching
1605
+ • KEYWORD-BASED: Best results with simple, space-separated terms like "react hooks" or "cli typescript"
1606
+
1607
+ Required when package name is unknown. If you have the exact package name, use npm_view_package directly. This reduces the need to use GitHub search when packages are found.`;
1531
1608
  const MAX_DESCRIPTION_LENGTH = 100;
1532
1609
  const MAX_KEYWORDS = 10;
1533
1610
  function registerNpmSearchTool(server) {
1534
- server.tool(TOOL_NAMES.NPM_PACKAGE_SEARCH, TOOL_DESCRIPTIONS[TOOL_NAMES.NPM_PACKAGE_SEARCH], {
1611
+ server.tool(TOOL_NAME$3, DESCRIPTION$3, {
1535
1612
  queries: z
1536
1613
  .union([z.string(), z.array(z.string())])
1537
- .describe('Package names or keywords to search for'),
1614
+ .describe('Package names or keywords to search for. NOTE: No boolean operators (AND/OR/NOT) supported - use simple space-separated keywords like "react hooks" or "typescript cli" for fuzzy matching.'),
1538
1615
  searchlimit: z
1539
1616
  .number()
1540
1617
  .int()
@@ -1542,10 +1619,10 @@ function registerNpmSearchTool(server) {
1542
1619
  .max(50)
1543
1620
  .optional()
1544
1621
  .default(20)
1545
- .describe('Max results per query (default: 20)'),
1622
+ .describe('Max results per query (default: 20, max: 50)'),
1546
1623
  }, {
1547
- title: 'Search NPM Packages by Name/Keyword',
1548
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.NPM_PACKAGE_SEARCH],
1624
+ title: TOOL_NAME$3,
1625
+ description: DESCRIPTION$3,
1549
1626
  readOnlyHint: true,
1550
1627
  destructiveHint: false,
1551
1628
  idempotentHint: true,
@@ -1575,12 +1652,10 @@ function registerNpmSearchTool(server) {
1575
1652
  results: deduplicatedPackages,
1576
1653
  });
1577
1654
  }
1578
- const suggestions = getNoResultsSuggestions(TOOL_NAMES.NPM_PACKAGE_SEARCH);
1579
- return createResult('No packages found', true, suggestions);
1655
+ return createResult('No packages found', true);
1580
1656
  }
1581
1657
  catch (error) {
1582
- const suggestions = getErrorSuggestions(TOOL_NAMES.NPM_PACKAGE_SEARCH);
1583
- return createResult(`Search failed: ${error.message}`, true, suggestions);
1658
+ return createResult('NPM package search failed - check search terms or try different keywords', true);
1584
1659
  }
1585
1660
  });
1586
1661
  }
@@ -1632,8 +1707,10 @@ function parseNpmSearchOutput(output) {
1632
1707
  }
1633
1708
  }
1634
1709
 
1710
+ const TOOL_NAME$2 = 'github_get_contents';
1711
+ const DESCRIPTION$2 = `Browse repository structure and verify file existence. Use before github_get_file_content to confirm files exist and understand organization, especially when the path is uncertain.`;
1635
1712
  function registerViewRepositoryStructureTool(server) {
1636
- server.tool(TOOL_NAMES.GITHUB_GET_CONTENTS, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_GET_CONTENTS], {
1713
+ server.tool(TOOL_NAME$2, DESCRIPTION$2, {
1637
1714
  owner: z
1638
1715
  .string()
1639
1716
  .min(1)
@@ -1663,8 +1740,8 @@ function registerViewRepositoryStructureTool(server) {
1663
1740
  .describe('Directory path within repository (e.g., "src/components", "packages/core"). ' +
1664
1741
  'Leave empty for root. Use previous results to navigate deeper.'),
1665
1742
  }, {
1666
- title: 'Browse Repository Structure and Browse Directories',
1667
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_GET_CONTENTS],
1743
+ title: TOOL_NAME$2,
1744
+ description: DESCRIPTION$2,
1668
1745
  readOnlyHint: true,
1669
1746
  destructiveHint: false,
1670
1747
  idempotentHint: true,
@@ -1694,48 +1771,7 @@ function registerViewRepositoryStructureTool(server) {
1694
1771
  }
1695
1772
  catch (error) {
1696
1773
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1697
- // Enhanced context-aware error suggestions
1698
- let suggestions = [];
1699
- const context = `${args.owner}/${args.repo}`;
1700
- if (errorMessage.includes('404') ||
1701
- errorMessage.includes('Not Found')) {
1702
- if (args.path) {
1703
- suggestions = [
1704
- `Try exploring root first: github_get_contents(owner: "${args.owner}", repo: "${args.repo}", branch: "${args.branch}", path: "")`,
1705
- `Search for similar paths: github_search_code with path filters`,
1706
- ];
1707
- }
1708
- else {
1709
- suggestions = [
1710
- `Search for repository: github_search_repositories(query: "${args.repo}")`,
1711
- `Verify owner exists: github_search_repositories(owner: "${args.owner}")`,
1712
- ];
1713
- }
1714
- }
1715
- else if (errorMessage.includes('403') ||
1716
- errorMessage.includes('Forbidden')) {
1717
- suggestions = [
1718
- `Check authentication status: api_status_check`,
1719
- `Try public repositories first to verify access`,
1720
- ];
1721
- }
1722
- else if (errorMessage.includes('rate limit')) {
1723
- suggestions = [
1724
- `Check rate limit status: api_status_check`,
1725
- `Wait before retrying or use authenticated requests`,
1726
- ];
1727
- }
1728
- else if (errorMessage.includes('invalid') ||
1729
- errorMessage.includes('branch')) {
1730
- suggestions = [
1731
- `Find valid branches: github_search_repositories(query: "${context}")`,
1732
- `Try common branches: main, master, develop`,
1733
- ];
1734
- }
1735
- else {
1736
- suggestions = getErrorSuggestions(TOOL_NAMES.GITHUB_GET_CONTENTS);
1737
- }
1738
- return createResult(`Repository exploration failed: ${errorMessage}. Context: ${context} on ${args.branch}${args.path ? ` at ${args.path}` : ''}`, true, suggestions);
1774
+ return createResult(`Repository exploration failed: ${errorMessage} - verify repository exists and is accessible`, true);
1739
1775
  }
1740
1776
  });
1741
1777
  }
@@ -1746,7 +1782,7 @@ function registerViewRepositoryStructureTool(server) {
1746
1782
  * - Smart branch detection: fetches repository default branch automatically
1747
1783
  * - Intelligent fallback: tries requested -> default -> common branches
1748
1784
  * - Input validation: prevents path traversal and validates GitHub naming
1749
- * - Enhanced error context: provides actionable suggestions for recovery
1785
+ * - Clear error context: provides descriptive error messages
1750
1786
  * - Efficient caching: avoids redundant API calls
1751
1787
  */
1752
1788
  async function viewRepositoryStructure(params) {
@@ -1789,19 +1825,17 @@ async function viewRepositoryStructure(params) {
1789
1825
  const errorMsg = lastError?.message || 'Unknown error';
1790
1826
  if (errorMsg.includes('404') || errorMsg.includes('Not Found')) {
1791
1827
  if (path) {
1792
- throw new Error(`Path "${path}" not found in repository ${owner}/${repo}. ` +
1793
- `Use github_get_contents with empty path first to discover the repository structure.`);
1828
+ throw new Error(`Path "${path}" not found - verify path exists or use github_search_code to find files`);
1794
1829
  }
1795
1830
  else {
1796
- throw new Error(`Repository ${owner}/${repo} not found or no accessible branches. ` +
1797
- `Tried branches: ${branchesToTry.join(', ')}`);
1831
+ throw new Error(`Repository not found: ${owner}/${repo} - verify owner/repo names or use github_search_repositories`);
1798
1832
  }
1799
1833
  }
1800
1834
  else if (errorMsg.includes('403') || errorMsg.includes('Forbidden')) {
1801
- throw new Error(`Access denied to repository ${owner}/${repo}`);
1835
+ throw new Error(`Access denied to repository ${owner}/${repo} - repository may be private or require authentication`);
1802
1836
  }
1803
1837
  else {
1804
- throw new Error(`Repository ${owner}/${repo} not found or path "${path}" doesn't exist`);
1838
+ throw new Error(`Access failed: ${owner}/${repo} - check connection or repository permissions`);
1805
1839
  }
1806
1840
  }
1807
1841
  // Limit total items to 100 for efficiency
@@ -1859,7 +1893,7 @@ async function viewRepositoryStructure(params) {
1859
1893
  }
1860
1894
  catch (error) {
1861
1895
  const errorMessage = error instanceof Error ? error.message : String(error);
1862
- return createErrorResult$1(`Repository access failed: ${owner}/${repo}${path ? ` at ${path}` : ''}`, new Error(errorMessage));
1896
+ return createErrorResult$1('Repository access failed - verify repository exists and check authentication', new Error(errorMessage));
1863
1897
  }
1864
1898
  });
1865
1899
  }
@@ -1895,12 +1929,22 @@ async function getSmartBranchFallback(owner, repo, requestedBranch) {
1895
1929
  return branches;
1896
1930
  }
1897
1931
 
1932
+ const TOOL_NAME$1 = 'github_search_issues';
1933
+ const DESCRIPTION$1 = `Find GitHub issues and problems with rich metadata (labels, reactions, comments, state). Discover pain points, feature requests, bug patterns, and community discussions.
1934
+
1935
+ SEARCH PATTERNS SUPPORTED:
1936
+ • BOOLEAN OPERATORS: "bug AND crash" (both required), "feature OR enhancement" (either term), "error NOT test" (excludes test)
1937
+ • EXACT PHRASES: "memory leak" (precise phrase matching)
1938
+ • GITHUB QUALIFIERS: Built-in support for "is:open", "label:bug", "author:username", etc.
1939
+ • COMBINABLE: Mix search terms with filters for surgical precision
1940
+
1941
+ Filter by state, labels, assignee, or date ranges for comprehensive issue discovery.`;
1898
1942
  function registerSearchGitHubIssuesTool(server) {
1899
- server.tool(TOOL_NAMES.GITHUB_SEARCH_ISSUES, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_ISSUES], {
1943
+ server.tool(TOOL_NAME$1, DESCRIPTION$1, {
1900
1944
  query: z
1901
1945
  .string()
1902
1946
  .min(1, 'Search query is required and cannot be empty')
1903
- .describe('Search query to find issues'),
1947
+ .describe('Search query with GITHUB SEARCH SYNTAX support. BOOLEAN OPERATORS: "bug AND crash" (both required), "feature OR enhancement" (either term), "error NOT test" (excludes). EXACT PHRASES: "memory leak" (precise matching). GITHUB QUALIFIERS: "is:open label:bug author:username" (native GitHub syntax). COMBINED: Mix boolean logic with qualifiers for precise issue discovery.'),
1904
1948
  owner: z
1905
1949
  .string()
1906
1950
  .min(1)
@@ -1993,26 +2037,26 @@ function registerSearchGitHubIssuesTool(server) {
1993
2037
  .max(50)
1994
2038
  .optional()
1995
2039
  .default(25)
1996
- .describe('Maximum results (default: 25)'),
2040
+ .describe('Maximum results (default: 25, max: 50)'),
1997
2041
  }, {
1998
- title: 'Search Issues for Problem Discovery and Solutions',
1999
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_ISSUES],
2042
+ title: TOOL_NAME$1,
2043
+ description: DESCRIPTION$1,
2000
2044
  readOnlyHint: true,
2001
2045
  destructiveHint: false,
2002
2046
  idempotentHint: true,
2003
2047
  openWorldHint: true,
2004
2048
  }, async (args) => {
2005
2049
  if (!args.query?.trim()) {
2006
- return createErrorResult$1('Search query is required and cannot be empty', new Error('Invalid query'));
2050
+ return createErrorResult$1('Search query is required and cannot be empty - provide keywords to search for issues', new Error('Invalid query'));
2007
2051
  }
2008
2052
  if (args.query.length > 256) {
2009
- return createErrorResult$1('Search query is too long. Please limit to 256 characters or less.', new Error('Query too long'));
2053
+ return createErrorResult$1('Search query is too long. Please limit to 256 characters or less - simplify your search terms', new Error('Query too long'));
2010
2054
  }
2011
2055
  try {
2012
2056
  return await searchGitHubIssues(args);
2013
2057
  }
2014
2058
  catch (error) {
2015
- return createErrorResult$1('Failed to search GitHub issues', error);
2059
+ return createErrorResult$1('GitHub issues search failed - check repository exists and query is valid', error);
2016
2060
  }
2017
2061
  });
2018
2062
  }
@@ -2105,15 +2149,20 @@ function buildGitHubIssuesAPICommand(params) {
2105
2149
  return { command: 'api', args: [apiPath] };
2106
2150
  }
2107
2151
 
2152
+ const TOOL_NAME = 'npm_view_package';
2153
+ const DESCRIPTION = `use npm view to get package metadata. You can get the package
2154
+ name from search results or user input. This tool is effieicnt since it gets important package metadata
2155
+ on a package fast to optimize tools calls. get package git repository path easily without need to search it (using repo/code search tools),
2156
+ exported files of a package are there, along with dependencies and version history.`;
2108
2157
  function registerNpmViewPackageTool(server) {
2109
- server.tool(TOOL_NAMES.NPM_VIEW_PACKAGE, TOOL_DESCRIPTIONS[TOOL_NAMES.NPM_VIEW_PACKAGE], {
2158
+ server.tool(TOOL_NAME, DESCRIPTION, {
2110
2159
  packageName: z
2111
2160
  .string()
2112
2161
  .min(1, 'Package name is required')
2113
2162
  .describe('NPM package name to analyze. Returns complete package context including exports (critical for GitHub file discovery), repository URL, dependencies, and version history.'),
2114
2163
  }, {
2115
- title: 'Get NPM Package Metadata',
2116
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.NPM_VIEW_PACKAGE],
2164
+ title: TOOL_NAME,
2165
+ description: DESCRIPTION,
2117
2166
  readOnlyHint: true,
2118
2167
  destructiveHint: false,
2119
2168
  idempotentHint: true,
@@ -2121,17 +2170,17 @@ function registerNpmViewPackageTool(server) {
2121
2170
  }, async (args) => {
2122
2171
  try {
2123
2172
  if (!args.packageName || args.packageName.trim() === '') {
2124
- return createResult('Package name is required', true);
2173
+ return createResult('Package name is required - provide a valid NPM package name', true);
2125
2174
  }
2126
2175
  // Basic package name validation
2127
2176
  if (!/^[a-z0-9@._/-]+$/.test(args.packageName)) {
2128
- return createResult('Invalid package name format', true);
2177
+ return createResult('Invalid package name format - use standard NPM naming (e.g., "package-name" or "@scope/package")', true);
2129
2178
  }
2130
2179
  const result = await npmViewPackage(args.packageName);
2131
2180
  return result;
2132
2181
  }
2133
2182
  catch (error) {
2134
- return createResult(`Failed to get package metadata: ${error.message}`, true);
2183
+ return createResult('Failed to get package metadata - verify package exists on NPM registry', true);
2135
2184
  }
2136
2185
  });
2137
2186
  }
@@ -2189,7 +2238,7 @@ async function npmViewPackage(packageName) {
2189
2238
  return createSuccessResult$1(viewResult);
2190
2239
  }
2191
2240
  catch (error) {
2192
- return createErrorResult$1('Failed to get npm package metadata', error);
2241
+ return createErrorResult$1('Failed to get npm package metadata - package may not exist or registry unavailable', error);
2193
2242
  }
2194
2243
  });
2195
2244
  }