octocode-mcp 2.3.1 → 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 (2) hide show
  1. package/build/index.js +434 -390
  2. package/package.json +27 -27
package/build/index.js CHANGED
@@ -7,86 +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 = `You are a smart code research assistant with semantic search capabilities using gh and npm cli under the hood.
26
- You are able to search code, repositories, issues, pull requests, commits, and packages.
27
- Using Github: You are able to search code, repositories, issues, pull requests, commits
28
- Using NPM: You are able to search packages, view package metadata, and view package dependencies.
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.
29
12
 
30
- - ALWAYS START with ${TOOL_NAMES.API_STATUS_CHECK} before evaluating any user query (check npm and GitHub authentication and use GitHub organizations)
31
- - Understand users query from semantic understanding and choose the right tools for the job.
13
+ IMPORTANT: check users github organizations and use them in github search tools if needed
32
14
 
33
- TOOLS SEARCH STRATEGY:
34
- - ${TOOL_NAMES.GITHUB_SEARCH_REPOS} - START SHALLOW & GO BROAD: Use topics for exploratory discovery, then narrow down
35
- - ${TOOL_NAMES.GITHUB_SEARCH_CODE} - Find implementation patterns with SMART BOOLEAN OPERATORS (AND, OR, NOT)
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
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)
40
19
 
41
- BEST PRACTICES:
42
- - Leverage smart boolean operators if possible in tools and advanced filters for smart research
43
- - For repo discovery: START SHALLOW with topics, then go BROAD to explore ecosystems
44
- - For code search: Be focused and specific with boolean operators
45
- - Always check documentation and examples when available
46
- - Add referenced and high quality research data from docs and quality code
47
- - Be creative and smart and use tools efficiently to gather most data
48
- - If query is short (e.g. where is the git repo of X package) make simple query and use tools to gather data fast
49
- - For broad research make steps and think about the best way to gather data`;
50
- const TOOL_DESCRIPTIONS = {
51
- [TOOL_NAMES.API_STATUS_CHECK]: `**MANDATORY FIRST STEP** - Check GitHub & NPM authentication status before any user query evaluation.
52
- Returns connectivity status and discovers user organizations - CRITICAL for accessing private/organizational repositories.
53
- **DO NOT PROCEED** with searches if both GitHub and NPM are disconnected.
54
- **USE ORGANIZATIONS** returned in 'github.organizations' array for targeted owner/org parameters in all search tools.
55
- Essential for enterprise code exploration and accessing company-specific repositories that require organizational membership.`,
56
- [TOOL_NAMES.NPM_PACKAGE_SEARCH]: `Search NPM packages by keyword. Use for package ecosystem discovery.`,
57
- [TOOL_NAMES.NPM_VIEW_PACKAGE]: `Get comprehensive package metadata essential for GitHub searches and code analysis. Returns complete package context:
58
- • repositoryGitUrl - Direct GitHub repo link for accurate searches
59
- • exports - Critical for discovering available files and import paths
60
- dependencies/devDependencies - Full ecosystem understanding
61
- • versions with dates - Historical evolution context
62
- The exports field is invaluable for GitHub file discovery - shows exact paths before fetching. Always use when finding packages in code.`,
63
- [TOOL_NAMES.GITHUB_SEARCH_CODE]: `SEMANTIC CODE DISCOVERY: Search code with SMART BOOLEAN OPERATORS (AND, OR, NOT).
64
- Prioritize intelligent boolean combinations: "logger AND debug NOT test", "config OR settings", "error handling AND typescript".
65
- Format: "term AND term" language:js path:src. Filters: owner/org/user, repo, extension, filename, language, path, size, limit, match scope.
66
- Use for finding actual implementation patterns and code examples.
67
- CRITICAL: When packages found in results or from user input, use ${TOOL_NAMES.NPM_VIEW_PACKAGE} for metadata/paths.`,
68
- [TOOL_NAMES.GITHUB_SEARCH_REPOS]: `Search repositories by name/description. START SHALLOW AND GO BROAD (contrary to code search).
69
- Use topics for EXPLORATORY discovery: topic:["cli","typescript","api"] to find ecosystem patterns.
70
- PRIMARY FILTERS work alone: owner, language, stars, topic, forks. SECONDARY FILTERS require query/primary filter: license, created, archived, includeForks, updated, visibility, match.
71
-
72
- SMART REPOS SEARCH 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".
73
-
74
- CRITICAL: When finding packages, use ${TOOL_NAMES.NPM_VIEW_PACKAGE} for metadata and repository paths.`,
75
- [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.`,
76
- [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.`,
77
- [TOOL_NAMES.GITHUB_SEARCH_ISSUES]: `Find GitHub issues and problems with rich metadata (labels, reactions, comments, state).
78
- Discover pain points, feature requests, bug patterns, and community discussions.
79
- Filter by state, labels, assignee, or date ranges. Use for understanding project health and common user issues.`,
80
- [TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS]: `Find pull requests and implementations with detailed metadata.
81
- Discover how features were implemented, code review patterns, and development workflows.
82
- Filter by state, author, reviewer, or merge status. Essential for understanding project development practices.`,
83
- [TOOL_NAMES.GITHUB_SEARCH_COMMITS]: `Search commit history. Use for understanding code evolution and development patterns.`,
84
- };
20
+ APPROACH:
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
27
+
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
34
+
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`;
85
40
 
86
- // CONSOLIDATED ERROR & SUCCESS HANDLING
87
41
  function createResult(data, isError = false, suggestions) {
88
42
  const text = isError
89
- ? `${data}${suggestions ? ` | Try: ${suggestions.join(', ')}` : ''}`
43
+ ? `${data}${''}`
90
44
  : JSON.stringify(data, null, 2);
91
45
  return {
92
46
  content: [{ type: 'text', text }],
@@ -110,56 +64,6 @@ function parseJsonResponse(responseText, fallback = null) {
110
64
  return { data: (fallback || responseText), parsed: false };
111
65
  }
112
66
  }
113
- /**
114
- * Generate fallback suggestions for no results - ensures no tool suggests itself
115
- */
116
- function getNoResultsSuggestions(currentTool) {
117
- const suggestions = [];
118
- // Tool-specific fallbacks
119
- switch (currentTool) {
120
- case TOOL_NAMES.GITHUB_SEARCH_REPOS:
121
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_CODE, TOOL_NAMES.NPM_PACKAGE_SEARCH);
122
- break;
123
- case TOOL_NAMES.GITHUB_SEARCH_CODE:
124
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_REPOS, TOOL_NAMES.GITHUB_SEARCH_ISSUES);
125
- break;
126
- case TOOL_NAMES.NPM_PACKAGE_SEARCH:
127
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_REPOS, TOOL_NAMES.GITHUB_SEARCH_CODE);
128
- break;
129
- case TOOL_NAMES.GITHUB_SEARCH_ISSUES:
130
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_CODE, TOOL_NAMES.GITHUB_SEARCH_REPOS);
131
- break;
132
- case TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS:
133
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_ISSUES, TOOL_NAMES.GITHUB_SEARCH_CODE);
134
- break;
135
- case TOOL_NAMES.GITHUB_SEARCH_COMMITS:
136
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_CODE, TOOL_NAMES.GITHUB_SEARCH_REPOS);
137
- break;
138
- case TOOL_NAMES.GITHUB_GET_CONTENTS:
139
- case TOOL_NAMES.GITHUB_GET_FILE_CONTENT:
140
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_REPOS, TOOL_NAMES.GITHUB_SEARCH_CODE);
141
- break;
142
- default:
143
- // Fallback for any other tools
144
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_REPOS, TOOL_NAMES.GITHUB_SEARCH_CODE);
145
- }
146
- return suggestions.slice(0, 3);
147
- }
148
- /**
149
- * Generate fallback suggestions for errors - ensures no tool suggests itself
150
- */
151
- function getErrorSuggestions(currentTool) {
152
- const suggestions = [];
153
- // Always suggest API status check first (unless it's the current tool)
154
- if (currentTool !== TOOL_NAMES.API_STATUS_CHECK) {
155
- suggestions.push(TOOL_NAMES.API_STATUS_CHECK);
156
- }
157
- // Add discovery alternatives
158
- if (currentTool !== TOOL_NAMES.GITHUB_SEARCH_REPOS) {
159
- suggestions.push(TOOL_NAMES.GITHUB_SEARCH_REPOS);
160
- }
161
- return suggestions.slice(0, 3);
162
- }
163
67
  /**
164
68
  * Determines if a string needs quoting for GitHub search
165
69
  */
@@ -238,8 +142,9 @@ async function executeNpmCommand(command, args = [], options = {}) {
238
142
  if (!isValidNpmCommand(command)) {
239
143
  return createErrorResult('Command not registered', new Error(`NPM command '${command}' is not in the allowed list`));
240
144
  }
241
- // Build command with validated prefix - no sanitization needed since we control the prefix
242
- 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(' ')}`;
243
148
  const executeNpmCommand = () => executeCommand(fullCommand, 'npm', options);
244
149
  if (options.cache) {
245
150
  const cacheKey = generateCacheKey('npm-exec', { command, args });
@@ -247,6 +152,17 @@ async function executeNpmCommand(command, args = [], options = {}) {
247
152
  }
248
153
  return executeNpmCommand();
249
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
+ }
250
166
  /**
251
167
  * Execute GitHub CLI commands safely by validating against allowed commands
252
168
  * Security: Only executes commands that start with "gh {ALLOWED_COMMAND}"
@@ -256,8 +172,9 @@ async function executeGitHubCommand(command, args = [], options = {}) {
256
172
  if (!isValidGhCommand(command)) {
257
173
  return createErrorResult('Command not registered', new Error(`GitHub command '${command}' is not in the allowed list`));
258
174
  }
259
- // Build command with validated prefix - no sanitization needed since we control the prefix
260
- 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(' ')}`;
261
178
  const executeGhCommand = () => executeCommand(fullCommand, 'github', options);
262
179
  if (options.cache) {
263
180
  const cacheKey = generateCacheKey('gh-exec', { command, args });
@@ -314,10 +231,13 @@ async function executeCommand(fullCommand, type, options = {}) {
314
231
  }
315
232
  }
316
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.`;
317
237
  function registerApiStatusCheckTool(server) {
318
- server.tool(TOOL_NAMES.API_STATUS_CHECK, TOOL_DESCRIPTIONS[TOOL_NAMES.API_STATUS_CHECK], {}, {
319
- title: TOOL_NAMES.API_STATUS_CHECK,
320
- 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,
321
241
  readOnlyHint: true,
322
242
  destructiveHint: false,
323
243
  idempotentHint: true,
@@ -433,64 +353,72 @@ function registerApiStatusCheckTool(server) {
433
353
  });
434
354
  }
435
355
  catch (error) {
436
- 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);
437
357
  }
438
358
  });
439
359
  }
440
360
 
441
- /**
442
- * Registers the GitHub Code Search tool with the MCP server.
443
- *
444
- * This tool provides semantic code search across GitHub repositories using the GitHub CLI.
445
- * It supports advanced search features like boolean operators, qualifiers, and filters.
446
- *
447
- * Key features:
448
- * - Boolean operator support (AND, OR, NOT) in queries
449
- * - GitHub qualifier support (language:, path:, etc.)
450
- * - Repository, owner, and organization filtering
451
- * - File type, size, and visibility filtering
452
- * - Multiple match scopes and filtering options
453
- */
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.`;
454
388
  function registerGitHubSearchCodeTool(server) {
455
- server.tool(TOOL_NAMES.GITHUB_SEARCH_CODE, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_CODE], {
389
+ server.tool(TOOL_NAME$8, DESCRIPTION$8, {
456
390
  query: z
457
391
  .string()
458
392
  .min(1)
459
- .describe('Search query with boolean operators (AND, OR, NOT) and qualifiers. ' +
460
- 'Examples: "react lifecycle", "error handling", "logger AND debug", "config OR settings", "main NOT test". ' +
461
- '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.'),
462
394
  owner: z
463
395
  .union([z.string(), z.array(z.string())])
464
396
  .optional()
465
- .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)'),
466
398
  repo: z
467
399
  .union([z.string(), z.array(z.string())])
468
400
  .optional()
469
- .describe('Specific repositories in "owner/repo" format. Can be a single repository string or array of repository strings. Requires owner parameter to be set.'),
401
+ .describe('Specific repositories in "owner/repo" format. Examples: "facebook/react", "microsoft/vscode". Requires owner parameter.'),
470
402
  language: z
471
403
  .string()
472
404
  .optional()
473
- .describe('Programming language filter (e.g., "javascript", "python", "go").'),
405
+ .describe('Programming language filter. Examples: "javascript", "python", "typescript", "go". Highly effective for targeted searches.'),
474
406
  extension: z
475
407
  .string()
476
408
  .optional()
477
- .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.'),
478
410
  filename: z
479
411
  .string()
480
412
  .optional()
481
- .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.'),
482
414
  path: z
483
415
  .string()
484
416
  .optional()
485
- .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.'),
486
418
  size: z
487
419
  .string()
488
420
  .optional()
489
421
  .describe('File size filter in KB with operators (e.g., ">100", "<50", "10..100").'),
490
- visibility: z
491
- .enum(['public', 'private', 'internal'])
492
- .optional()
493
- .describe('Repository visibility filter. "public" for public repos, "private" for private repos you have access to.'),
494
422
  limit: z
495
423
  .number()
496
424
  .int()
@@ -502,10 +430,14 @@ function registerGitHubSearchCodeTool(server) {
502
430
  match: z
503
431
  .union([z.enum(['file', 'path']), z.array(z.enum(['file', 'path']))])
504
432
  .optional()
505
- .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.'),
506
438
  }, {
507
- title: TOOL_NAMES.GITHUB_SEARCH_CODE,
508
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_CODE],
439
+ title: TOOL_NAME$8,
440
+ description: DESCRIPTION$8,
509
441
  readOnlyHint: true,
510
442
  destructiveHint: false,
511
443
  idempotentHint: true,
@@ -513,7 +445,7 @@ function registerGitHubSearchCodeTool(server) {
513
445
  }, async (args) => {
514
446
  try {
515
447
  if (args.repo && !args.owner) {
516
- return createResult('Repository search requires owner parameter', true);
448
+ return createResult('Repository search requires owner parameter - specify owner when searching specific repositories', true);
517
449
  }
518
450
  const result = await searchGitHubCode(args);
519
451
  if (result.isError) {
@@ -525,113 +457,206 @@ function registerGitHubSearchCodeTool(server) {
525
457
  const items = Array.isArray(codeResults) ? codeResults : [];
526
458
  return createSuccessResult$1({
527
459
  query: args.query,
460
+ processed_query: parseSearchQuery(args.query, args),
528
461
  total_count: items.length,
529
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
+ },
530
469
  });
531
470
  }
532
471
  catch (error) {
533
- return createErrorResult$1('Code search failed', error);
472
+ return createErrorResult$1('GitHub code search failed - check repository access or simplify query', error);
534
473
  }
535
474
  });
536
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}`);
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);
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;
614
+ }
537
615
  async function searchGitHubCode(params) {
538
616
  const cacheKey = generateCacheKey('gh-code', params);
539
617
  return withCache(cacheKey, async () => {
540
618
  try {
541
- const args = ['code'];
542
- // Build the main query - preserve boolean operators and GitHub syntax
543
- let query = params.query;
544
- // Add path filter to query if provided (GitHub search syntax)
545
- if (params.path) {
546
- query = `${query} path:${params.path}`;
547
- }
548
- // Add visibility filter to query if provided
549
- if (params.visibility) {
550
- query = `${query} visibility:${params.visibility}`;
551
- }
552
- args.push(query);
553
- // Add command-line filters
554
- if (params.language)
555
- args.push(`--language=${params.language}`);
556
- if (params.extension)
557
- args.push(`--extension=${params.extension}`);
558
- if (params.filename)
559
- args.push(`--filename=${params.filename}`);
560
- if (params.size)
561
- args.push(`--size=${params.size}`);
562
- if (params.limit)
563
- args.push(`--limit=${params.limit}`);
564
- // Handle match parameter - can be string or array
565
- // Note: GitHub search API doesn't support multiple match types in a single query
566
- if (params.match) {
567
- const matchValues = Array.isArray(params.match)
568
- ? params.match
569
- : [params.match];
570
- // GitHub API limitation: can't use both in:file and in:path in same query
571
- // Use the first match type when multiple are provided
572
- const matchValue = matchValues[0];
573
- args.push(`--match=${matchValue}`);
574
- }
575
- // Handle owner parameter - can be string or array
576
- if (params.owner && !params.repo) {
577
- const ownerValues = Array.isArray(params.owner)
578
- ? params.owner
579
- : [params.owner];
580
- ownerValues.forEach(owner => args.push(`--owner=${owner}`));
581
- }
582
- // Handle repository filters
583
- if (params.owner && params.repo) {
584
- const owners = Array.isArray(params.owner)
585
- ? params.owner
586
- : [params.owner];
587
- const repos = Array.isArray(params.repo) ? params.repo : [params.repo];
588
- // Create repo filters for each owner/repo combination
589
- owners.forEach(owner => {
590
- repos.forEach(repo => {
591
- // Handle both "owner/repo" format and just "repo" format
592
- if (repo.includes('/')) {
593
- args.push(`--repo=${repo}`);
594
- }
595
- else {
596
- args.push(`--repo=${owner}/${repo}`);
597
- }
598
- });
599
- });
600
- }
601
- // JSON output with all available fields
602
- args.push('--json=repository,path,textMatches,sha,url');
619
+ const args = buildGitHubCliArgs(params);
603
620
  const result = await executeGitHubCommand('search', args, {
604
621
  cache: false,
605
622
  });
606
623
  return result;
607
624
  }
608
625
  catch (error) {
609
- return createErrorResult$1('Failed to execute search command', error);
626
+ return createErrorResult$1('Code search command failed - verify GitHub CLI is authenticated', error);
610
627
  }
611
628
  });
612
629
  }
613
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.`;
614
633
  function registerFetchGitHubFileContentTool(server) {
615
- server.tool(TOOL_NAMES.GITHUB_GET_FILE_CONTENT, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_GET_FILE_CONTENT], {
634
+ server.tool(TOOL_NAME$7, DESCRIPTION$7, {
616
635
  owner: z
617
636
  .string()
618
637
  .min(1)
638
+ .max(100)
639
+ .regex(/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/)
619
640
  .describe(`Repository owner/organization (e.g., 'microsoft', 'facebook')`),
620
641
  repo: z
621
642
  .string()
622
643
  .min(1)
644
+ .max(100)
645
+ .regex(/^[a-zA-Z0-9._-]+$/)
623
646
  .describe(`Repository name (e.g., 'vscode', 'react'). Case-sensitive.`),
624
647
  branch: z
625
648
  .string()
626
649
  .min(1)
650
+ .max(255)
651
+ .regex(/^[^\s]+$/)
627
652
  .describe(`Branch name (e.g., 'main', 'master'). Auto-fallback to common branches if not found.`),
628
653
  filePath: z
629
654
  .string()
630
655
  .min(1)
631
656
  .describe(`File path from repository root (e.g., 'README.md', 'src/index.js'). Use github_get_contents to explore structure.`),
632
657
  }, {
633
- title: TOOL_NAMES.GITHUB_GET_FILE_CONTENT,
634
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_GET_FILE_CONTENT],
658
+ title: TOOL_NAME$7,
659
+ description: DESCRIPTION$7,
635
660
  readOnlyHint: true,
636
661
  destructiveHint: false,
637
662
  idempotentHint: true,
@@ -666,26 +691,7 @@ function registerFetchGitHubFileContentTool(server) {
666
691
  return result;
667
692
  }
668
693
  catch (error) {
669
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
670
- let suggestions = [];
671
- if (errorMessage.includes('404') ||
672
- errorMessage.includes('Not Found')) {
673
- suggestions = [
674
- TOOL_NAMES.GITHUB_GET_CONTENTS,
675
- TOOL_NAMES.GITHUB_SEARCH_CODE,
676
- ];
677
- }
678
- else if (errorMessage.includes('403') ||
679
- errorMessage.includes('Forbidden')) {
680
- suggestions = [TOOL_NAMES.API_STATUS_CHECK];
681
- }
682
- else if (errorMessage.includes('branch')) {
683
- suggestions = [TOOL_NAMES.GITHUB_GET_CONTENTS];
684
- }
685
- else {
686
- suggestions = getErrorSuggestions(TOOL_NAMES.GITHUB_GET_FILE_CONTENT);
687
- }
688
- 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);
689
695
  }
690
696
  });
691
697
  }
@@ -718,19 +724,19 @@ async function fetchGitHubFileContent(params) {
718
724
  }
719
725
  // Handle common errors
720
726
  if (errorMsg.includes('404')) {
721
- 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));
722
728
  }
723
729
  else if (errorMsg.includes('403')) {
724
- 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}`));
725
731
  }
726
732
  else {
727
- 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));
728
734
  }
729
735
  }
730
736
  return await processFileContent(result, owner, repo, branch, filePath);
731
737
  }
732
738
  catch (error) {
733
- return createErrorResult$1(`Unexpected error fetching file: ${filePath}`, error);
739
+ return createErrorResult$1('Unexpected error during file fetch - check connection and permissions', error);
734
740
  }
735
741
  });
736
742
  }
@@ -740,30 +746,30 @@ async function processFileContent(result, owner, repo, branch, filePath) {
740
746
  const fileData = JSON.parse(execResult.result);
741
747
  // Check if it's a directory
742
748
  if (Array.isArray(fileData)) {
743
- 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));
744
750
  }
745
751
  const fileSize = fileData.size || 0;
746
752
  const MAX_FILE_SIZE = 500 * 1024; // 500KB limit for simplicity
747
753
  // Check file size
748
754
  if (fileSize > MAX_FILE_SIZE) {
749
- 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`));
750
756
  }
751
757
  // Get and decode content
752
758
  const base64Content = fileData.content?.replace(/\s/g, ''); // Remove all whitespace
753
759
  if (!base64Content) {
754
- 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));
755
761
  }
756
762
  let decodedContent;
757
763
  try {
758
764
  const buffer = Buffer.from(base64Content, 'base64');
759
765
  // Simple binary check - look for null bytes
760
766
  if (buffer.indexOf(0) !== -1) {
761
- 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));
762
768
  }
763
769
  decodedContent = buffer.toString('utf-8');
764
770
  }
765
771
  catch (decodeError) {
766
- 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));
767
773
  }
768
774
  // Return simplified response
769
775
  const response = {
@@ -781,8 +787,17 @@ async function processFileContent(result, owner, repo, branch, filePath) {
781
787
  return createSuccessResult$1(response);
782
788
  }
783
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`;
784
799
  function registerSearchGitHubReposTool(server) {
785
- server.tool(TOOL_NAMES.GITHUB_SEARCH_REPOS, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_REPOS], {
800
+ server.tool(TOOL_NAME$6, DESCRIPTION$6, {
786
801
  query: z
787
802
  .string()
788
803
  .optional()
@@ -791,52 +806,66 @@ function registerSearchGitHubReposTool(server) {
791
806
  owner: z
792
807
  .string()
793
808
  .optional()
794
- .describe('Repository owner/organization. PRIMARY FILTER - works alone.'),
809
+ .describe('Repository owner/organization (e.g., "microsoft", "facebook").'),
795
810
  language: z
796
811
  .string()
797
812
  .optional()
798
- .describe('Programming language. PRIMARY FILTER - works alone.'),
813
+ .describe('Programming language (e.g., "javascript", "python", "go").'),
799
814
  stars: z
800
815
  .string()
801
816
  .optional()
802
- .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.'),
803
818
  topic: z
804
819
  .array(z.string())
805
820
  .optional()
806
- .describe('Filter by topics. PRIMARY FILTER - works alone.'),
807
- forks: z
821
+ .describe('Filter by topics (e.g., ["cli", "typescript", "api"]).'),
822
+ forks: z.number().optional().describe('Exact forks count.'),
823
+ numberOfTopics: z
808
824
  .number()
809
825
  .optional()
810
- .describe('Exact forks count. PRIMARY FILTER - works alone.'),
826
+ .describe('Filter on number of topics.'),
811
827
  // SECONDARY FILTERS (require query or primary filter)
812
828
  license: z
813
829
  .array(z.string())
814
830
  .optional()
815
- .describe('License types. REQUIRES query or primary filter.'),
831
+ .describe('License types (e.g., ["mit", "apache-2.0"]).'),
816
832
  match: z
817
833
  .enum(['name', 'description', 'readme'])
818
834
  .optional()
819
- .describe('Search scope. REQUIRES query.'),
835
+ .describe('Search scope: "name", "description", or "readme".'),
820
836
  visibility: z
821
837
  .enum(['public', 'private', 'internal'])
822
838
  .optional()
823
- .describe('Repository visibility. REQUIRES query or primary filter.'),
839
+ .describe('Repository visibility filter.'),
824
840
  created: z
825
841
  .string()
826
842
  .optional()
827
- .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".'),
828
844
  updated: z
829
845
  .string()
830
846
  .optional()
831
- .describe('Updated date filter. REQUIRES query or primary filter.'),
832
- archived: z
833
- .boolean()
834
- .optional()
835
- .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.'),
836
849
  includeForks: z
837
850
  .enum(['false', 'true', 'only'])
838
851
  .optional()
839
- .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").'),
840
869
  // Sorting and limits
841
870
  sort: z
842
871
  .enum(['forks', 'help-wanted-issues', 'stars', 'updated', 'best-match'])
@@ -857,8 +886,8 @@ function registerSearchGitHubReposTool(server) {
857
886
  .default(25)
858
887
  .describe('Maximum results (default: 25, max: 50)'),
859
888
  }, {
860
- title: TOOL_NAMES.GITHUB_SEARCH_REPOS,
861
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_REPOS],
889
+ title: TOOL_NAME$6,
890
+ description: DESCRIPTION$6,
862
891
  readOnlyHint: true,
863
892
  destructiveHint: false,
864
893
  idempotentHint: true,
@@ -880,7 +909,7 @@ function registerSearchGitHubReposTool(server) {
880
909
  return result;
881
910
  }
882
911
  catch (error) {
883
- 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);
884
913
  }
885
914
  });
886
915
  }
@@ -947,7 +976,7 @@ async function searchGitHubRepos(params) {
947
976
  isPrivate: repo.isPrivate || false,
948
977
  isArchived: repo.isArchived || false,
949
978
  isFork: repo.isFork || false,
950
- topics: [], // Topics not available via CLI JSON output
979
+ topics: [], // GitHub CLI search repos doesn't provide topics in JSON output
951
980
  license: repo.license?.name || null,
952
981
  hasIssues: repo.hasIssues || false,
953
982
  openIssuesCount: repo.openIssuesCount || 0,
@@ -971,17 +1000,11 @@ async function searchGitHubRepos(params) {
971
1000
  }
972
1001
  : {
973
1002
  repositories: [],
974
- suggestions: [
975
- `${TOOL_NAMES.NPM_PACKAGE_SEARCH} "${params.query || 'package'}"`,
976
- `${TOOL_NAMES.GITHUB_SEARCH_CODE} "${params.query || 'code'}"`,
977
- 'Try broader search terms',
978
- 'Check spelling and try synonyms',
979
- ],
980
1003
  }),
981
1004
  });
982
1005
  }
983
1006
  catch (error) {
984
- return createErrorResult$1('Failed to search GitHub repositories', error);
1007
+ return createErrorResult$1('GitHub repository search failed - verify connection or try simpler query', error);
985
1008
  }
986
1009
  });
987
1010
  }
@@ -1014,12 +1037,22 @@ function buildGitHubReposSearchCommand(params) {
1014
1037
  });
1015
1038
  }
1016
1039
  else {
1017
- // For simple queries, use quoting logic
1018
- const queryString = needsQuoting(query) ? `"${query}"` : query;
1019
- 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
+ });
1020
1052
  }
1021
1053
  }
1022
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
1023
1056
  args.push('--json', 'name,fullName,description,language,stargazersCount,forksCount,updatedAt,createdAt,url,owner,isPrivate,license,hasIssues,openIssuesCount,isArchived,isFork,visibility');
1024
1057
  // PRIMARY FILTERS - Handle owner as single string (BaseSearchParams) or array
1025
1058
  if (params.owner) {
@@ -1034,6 +1067,8 @@ function buildGitHubReposSearchCommand(params) {
1034
1067
  args.push(`--forks=${params.forks}`);
1035
1068
  if (params.topic && params.topic.length > 0)
1036
1069
  args.push(`--topic=${params.topic.join(',')}`);
1070
+ if (params.numberOfTopics !== undefined)
1071
+ args.push(`--number-topics=${params.numberOfTopics}`);
1037
1072
  // Only add stars filter if it's a valid numeric value or range
1038
1073
  if (params.stars !== undefined &&
1039
1074
  params.stars !== '*' &&
@@ -1042,7 +1077,7 @@ function buildGitHubReposSearchCommand(params) {
1042
1077
  const starsValue = params.stars.trim();
1043
1078
  const isValidStars = /^(\d+|>\d+|<\d+|\d+\.\.\d+|>=\d+|<=\d+)$/.test(starsValue);
1044
1079
  if (isValidStars) {
1045
- args.push(`--stars="${params.stars}"`);
1080
+ args.push(`--stars=${params.stars}`);
1046
1081
  }
1047
1082
  }
1048
1083
  // SECONDARY FILTERS - only add if we have primary filters
@@ -1060,6 +1095,14 @@ function buildGitHubReposSearchCommand(params) {
1060
1095
  args.push(`--updated="${params.updated}"`);
1061
1096
  if (params.visibility)
1062
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}`);
1063
1106
  // SORTING AND LIMITS
1064
1107
  if (params.limit)
1065
1108
  args.push(`--limit=${params.limit}`);
@@ -1073,12 +1116,14 @@ function buildGitHubReposSearchCommand(params) {
1073
1116
  return { command: 'search', args };
1074
1117
  }
1075
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.`;
1076
1121
  function registerSearchGitHubCommitsTool(server) {
1077
- server.tool(TOOL_NAMES.GITHUB_SEARCH_COMMITS, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_COMMITS], {
1122
+ server.tool(TOOL_NAME$5, DESCRIPTION$5, {
1078
1123
  query: z
1079
1124
  .string()
1080
1125
  .optional()
1081
- .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.'),
1082
1127
  // Basic filters
1083
1128
  owner: z
1084
1129
  .string()
@@ -1135,10 +1180,10 @@ function registerSearchGitHubCommitsTool(server) {
1135
1180
  .max(50)
1136
1181
  .optional()
1137
1182
  .default(25)
1138
- .describe('Maximum results (default: 25)'),
1183
+ .describe('Maximum results (default: 25, max: 50)'),
1139
1184
  }, {
1140
- title: TOOL_NAMES.GITHUB_SEARCH_COMMITS,
1141
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_COMMITS],
1185
+ title: TOOL_NAME$5,
1186
+ description: DESCRIPTION$5,
1142
1187
  readOnlyHint: true,
1143
1188
  destructiveHint: false,
1144
1189
  idempotentHint: true,
@@ -1157,7 +1202,7 @@ function registerSearchGitHubCommitsTool(server) {
1157
1202
  return result;
1158
1203
  }
1159
1204
  catch (error) {
1160
- 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);
1161
1206
  }
1162
1207
  });
1163
1208
  }
@@ -1252,17 +1297,10 @@ async function searchGitHubCommits(params) {
1252
1297
  query: params.query,
1253
1298
  total: 0,
1254
1299
  commits: [],
1255
- suggestions: [
1256
- `${TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS} "${params.query || 'pr'}"`,
1257
- `${TOOL_NAMES.GITHUB_SEARCH_ISSUES} "${params.query || 'issue'}"`,
1258
- `${TOOL_NAMES.GITHUB_SEARCH_CODE} "${params.query || 'code'}"`,
1259
- 'Try broader search terms',
1260
- 'Check spelling and try synonyms',
1261
- ],
1262
1300
  });
1263
1301
  }
1264
1302
  catch (error) {
1265
- 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);
1266
1304
  }
1267
1305
  });
1268
1306
  }
@@ -1352,12 +1390,22 @@ function buildGitHubCommitsSearchCommand(params) {
1352
1390
  }
1353
1391
 
1354
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.`;
1355
1403
  function registerSearchGitHubPullRequestsTool(server) {
1356
- server.tool(TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS], {
1404
+ server.tool(TOOL_NAME$4, DESCRIPTION$4, {
1357
1405
  query: z
1358
1406
  .string()
1359
1407
  .min(1, 'Search query is required and cannot be empty')
1360
- .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.'),
1361
1409
  owner: z.string().optional().describe('Repository owner/organization'),
1362
1410
  repo: z.string().optional().describe('Repository name'),
1363
1411
  author: z.string().optional().describe('Filter by pull request author'),
@@ -1382,6 +1430,15 @@ function registerSearchGitHubPullRequestsTool(server) {
1382
1430
  mergedAt: z.string().optional().describe('Filter by merged date'),
1383
1431
  closed: z.string().optional().describe('Filter by closed date'),
1384
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'),
1385
1442
  limit: z
1386
1443
  .number()
1387
1444
  .int()
@@ -1389,7 +1446,7 @@ function registerSearchGitHubPullRequestsTool(server) {
1389
1446
  .max(50)
1390
1447
  .optional()
1391
1448
  .default(25)
1392
- .describe('Maximum results (default: 25)'),
1449
+ .describe('Maximum results (default: 25, max: 50)'),
1393
1450
  sort: z
1394
1451
  .enum([
1395
1452
  'comments',
@@ -1412,24 +1469,24 @@ function registerSearchGitHubPullRequestsTool(server) {
1412
1469
  .default('desc')
1413
1470
  .describe('Order (default: desc)'),
1414
1471
  }, {
1415
- title: TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS,
1416
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS],
1472
+ title: TOOL_NAME$4,
1473
+ description: DESCRIPTION$4,
1417
1474
  readOnlyHint: true,
1418
1475
  destructiveHint: false,
1419
1476
  idempotentHint: true,
1420
1477
  openWorldHint: true,
1421
1478
  }, async (args) => {
1422
1479
  if (!args.query?.trim()) {
1423
- 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'));
1424
1481
  }
1425
1482
  if (args.query.length > 256) {
1426
- 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'));
1427
1484
  }
1428
1485
  try {
1429
1486
  return await searchGitHubPullRequests(args);
1430
1487
  }
1431
1488
  catch (error) {
1432
- 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);
1433
1490
  }
1434
1491
  });
1435
1492
  }
@@ -1521,6 +1578,12 @@ function buildGitHubPullRequestsAPICommand(params) {
1521
1578
  queryParts.push(`merged:${params.mergedAt}`);
1522
1579
  if (params.draft !== undefined)
1523
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}`);
1524
1587
  // Add type qualifier to search only pull requests
1525
1588
  queryParts.push('type:pr');
1526
1589
  const query = queryParts.filter(Boolean).join(' ');
@@ -1533,13 +1596,22 @@ function buildGitHubPullRequestsAPICommand(params) {
1533
1596
  return { command: 'api', args: [apiPath] };
1534
1597
  }
1535
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.`;
1536
1608
  const MAX_DESCRIPTION_LENGTH = 100;
1537
1609
  const MAX_KEYWORDS = 10;
1538
1610
  function registerNpmSearchTool(server) {
1539
- server.tool(TOOL_NAMES.NPM_PACKAGE_SEARCH, TOOL_DESCRIPTIONS[TOOL_NAMES.NPM_PACKAGE_SEARCH], {
1611
+ server.tool(TOOL_NAME$3, DESCRIPTION$3, {
1540
1612
  queries: z
1541
1613
  .union([z.string(), z.array(z.string())])
1542
- .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.'),
1543
1615
  searchlimit: z
1544
1616
  .number()
1545
1617
  .int()
@@ -1547,10 +1619,10 @@ function registerNpmSearchTool(server) {
1547
1619
  .max(50)
1548
1620
  .optional()
1549
1621
  .default(20)
1550
- .describe('Max results per query (default: 20)'),
1622
+ .describe('Max results per query (default: 20, max: 50)'),
1551
1623
  }, {
1552
- title: TOOL_NAMES.NPM_PACKAGE_SEARCH,
1553
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.NPM_PACKAGE_SEARCH],
1624
+ title: TOOL_NAME$3,
1625
+ description: DESCRIPTION$3,
1554
1626
  readOnlyHint: true,
1555
1627
  destructiveHint: false,
1556
1628
  idempotentHint: true,
@@ -1580,12 +1652,10 @@ function registerNpmSearchTool(server) {
1580
1652
  results: deduplicatedPackages,
1581
1653
  });
1582
1654
  }
1583
- const suggestions = getNoResultsSuggestions(TOOL_NAMES.NPM_PACKAGE_SEARCH);
1584
- return createResult('No packages found', true, suggestions);
1655
+ return createResult('No packages found', true);
1585
1656
  }
1586
1657
  catch (error) {
1587
- const suggestions = getErrorSuggestions(TOOL_NAMES.NPM_PACKAGE_SEARCH);
1588
- return createResult(`Search failed: ${error.message}`, true, suggestions);
1658
+ return createResult('NPM package search failed - check search terms or try different keywords', true);
1589
1659
  }
1590
1660
  });
1591
1661
  }
@@ -1637,8 +1707,10 @@ function parseNpmSearchOutput(output) {
1637
1707
  }
1638
1708
  }
1639
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.`;
1640
1712
  function registerViewRepositoryStructureTool(server) {
1641
- server.tool(TOOL_NAMES.GITHUB_GET_CONTENTS, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_GET_CONTENTS], {
1713
+ server.tool(TOOL_NAME$2, DESCRIPTION$2, {
1642
1714
  owner: z
1643
1715
  .string()
1644
1716
  .min(1)
@@ -1668,8 +1740,8 @@ function registerViewRepositoryStructureTool(server) {
1668
1740
  .describe('Directory path within repository (e.g., "src/components", "packages/core"). ' +
1669
1741
  'Leave empty for root. Use previous results to navigate deeper.'),
1670
1742
  }, {
1671
- title: TOOL_NAMES.GITHUB_GET_CONTENTS,
1672
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_GET_CONTENTS],
1743
+ title: TOOL_NAME$2,
1744
+ description: DESCRIPTION$2,
1673
1745
  readOnlyHint: true,
1674
1746
  destructiveHint: false,
1675
1747
  idempotentHint: true,
@@ -1699,48 +1771,7 @@ function registerViewRepositoryStructureTool(server) {
1699
1771
  }
1700
1772
  catch (error) {
1701
1773
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1702
- // Enhanced context-aware error suggestions
1703
- let suggestions = [];
1704
- const context = `${args.owner}/${args.repo}`;
1705
- if (errorMessage.includes('404') ||
1706
- errorMessage.includes('Not Found')) {
1707
- if (args.path) {
1708
- suggestions = [
1709
- `Try exploring root first: github_get_contents(owner: "${args.owner}", repo: "${args.repo}", branch: "${args.branch}", path: "")`,
1710
- `Search for similar paths: github_search_code with path filters`,
1711
- ];
1712
- }
1713
- else {
1714
- suggestions = [
1715
- `Search for repository: github_search_repositories(query: "${args.repo}")`,
1716
- `Verify owner exists: github_search_repositories(owner: "${args.owner}")`,
1717
- ];
1718
- }
1719
- }
1720
- else if (errorMessage.includes('403') ||
1721
- errorMessage.includes('Forbidden')) {
1722
- suggestions = [
1723
- `Check authentication status: api_status_check`,
1724
- `Try public repositories first to verify access`,
1725
- ];
1726
- }
1727
- else if (errorMessage.includes('rate limit')) {
1728
- suggestions = [
1729
- `Check rate limit status: api_status_check`,
1730
- `Wait before retrying or use authenticated requests`,
1731
- ];
1732
- }
1733
- else if (errorMessage.includes('invalid') ||
1734
- errorMessage.includes('branch')) {
1735
- suggestions = [
1736
- `Find valid branches: github_search_repositories(query: "${context}")`,
1737
- `Try common branches: main, master, develop`,
1738
- ];
1739
- }
1740
- else {
1741
- suggestions = getErrorSuggestions(TOOL_NAMES.GITHUB_GET_CONTENTS);
1742
- }
1743
- 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);
1744
1775
  }
1745
1776
  });
1746
1777
  }
@@ -1751,7 +1782,7 @@ function registerViewRepositoryStructureTool(server) {
1751
1782
  * - Smart branch detection: fetches repository default branch automatically
1752
1783
  * - Intelligent fallback: tries requested -> default -> common branches
1753
1784
  * - Input validation: prevents path traversal and validates GitHub naming
1754
- * - Enhanced error context: provides actionable suggestions for recovery
1785
+ * - Clear error context: provides descriptive error messages
1755
1786
  * - Efficient caching: avoids redundant API calls
1756
1787
  */
1757
1788
  async function viewRepositoryStructure(params) {
@@ -1794,19 +1825,17 @@ async function viewRepositoryStructure(params) {
1794
1825
  const errorMsg = lastError?.message || 'Unknown error';
1795
1826
  if (errorMsg.includes('404') || errorMsg.includes('Not Found')) {
1796
1827
  if (path) {
1797
- throw new Error(`Path "${path}" not found in repository ${owner}/${repo}. ` +
1798
- `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`);
1799
1829
  }
1800
1830
  else {
1801
- throw new Error(`Repository ${owner}/${repo} not found or no accessible branches. ` +
1802
- `Tried branches: ${branchesToTry.join(', ')}`);
1831
+ throw new Error(`Repository not found: ${owner}/${repo} - verify owner/repo names or use github_search_repositories`);
1803
1832
  }
1804
1833
  }
1805
1834
  else if (errorMsg.includes('403') || errorMsg.includes('Forbidden')) {
1806
- 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`);
1807
1836
  }
1808
1837
  else {
1809
- 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`);
1810
1839
  }
1811
1840
  }
1812
1841
  // Limit total items to 100 for efficiency
@@ -1864,7 +1893,7 @@ async function viewRepositoryStructure(params) {
1864
1893
  }
1865
1894
  catch (error) {
1866
1895
  const errorMessage = error instanceof Error ? error.message : String(error);
1867
- 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));
1868
1897
  }
1869
1898
  });
1870
1899
  }
@@ -1900,12 +1929,22 @@ async function getSmartBranchFallback(owner, repo, requestedBranch) {
1900
1929
  return branches;
1901
1930
  }
1902
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.`;
1903
1942
  function registerSearchGitHubIssuesTool(server) {
1904
- server.tool(TOOL_NAMES.GITHUB_SEARCH_ISSUES, TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_ISSUES], {
1943
+ server.tool(TOOL_NAME$1, DESCRIPTION$1, {
1905
1944
  query: z
1906
1945
  .string()
1907
1946
  .min(1, 'Search query is required and cannot be empty')
1908
- .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.'),
1909
1948
  owner: z
1910
1949
  .string()
1911
1950
  .min(1)
@@ -1998,26 +2037,26 @@ function registerSearchGitHubIssuesTool(server) {
1998
2037
  .max(50)
1999
2038
  .optional()
2000
2039
  .default(25)
2001
- .describe('Maximum results (default: 25)'),
2040
+ .describe('Maximum results (default: 25, max: 50)'),
2002
2041
  }, {
2003
- title: TOOL_NAMES.GITHUB_SEARCH_ISSUES,
2004
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_ISSUES],
2042
+ title: TOOL_NAME$1,
2043
+ description: DESCRIPTION$1,
2005
2044
  readOnlyHint: true,
2006
2045
  destructiveHint: false,
2007
2046
  idempotentHint: true,
2008
2047
  openWorldHint: true,
2009
2048
  }, async (args) => {
2010
2049
  if (!args.query?.trim()) {
2011
- 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'));
2012
2051
  }
2013
2052
  if (args.query.length > 256) {
2014
- 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'));
2015
2054
  }
2016
2055
  try {
2017
2056
  return await searchGitHubIssues(args);
2018
2057
  }
2019
2058
  catch (error) {
2020
- 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);
2021
2060
  }
2022
2061
  });
2023
2062
  }
@@ -2110,15 +2149,20 @@ function buildGitHubIssuesAPICommand(params) {
2110
2149
  return { command: 'api', args: [apiPath] };
2111
2150
  }
2112
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.`;
2113
2157
  function registerNpmViewPackageTool(server) {
2114
- server.tool(TOOL_NAMES.NPM_VIEW_PACKAGE, TOOL_DESCRIPTIONS[TOOL_NAMES.NPM_VIEW_PACKAGE], {
2158
+ server.tool(TOOL_NAME, DESCRIPTION, {
2115
2159
  packageName: z
2116
2160
  .string()
2117
2161
  .min(1, 'Package name is required')
2118
2162
  .describe('NPM package name to analyze. Returns complete package context including exports (critical for GitHub file discovery), repository URL, dependencies, and version history.'),
2119
2163
  }, {
2120
- title: TOOL_NAMES.NPM_VIEW_PACKAGE,
2121
- description: TOOL_DESCRIPTIONS[TOOL_NAMES.NPM_VIEW_PACKAGE],
2164
+ title: TOOL_NAME,
2165
+ description: DESCRIPTION,
2122
2166
  readOnlyHint: true,
2123
2167
  destructiveHint: false,
2124
2168
  idempotentHint: true,
@@ -2126,17 +2170,17 @@ function registerNpmViewPackageTool(server) {
2126
2170
  }, async (args) => {
2127
2171
  try {
2128
2172
  if (!args.packageName || args.packageName.trim() === '') {
2129
- return createResult('Package name is required', true);
2173
+ return createResult('Package name is required - provide a valid NPM package name', true);
2130
2174
  }
2131
2175
  // Basic package name validation
2132
2176
  if (!/^[a-z0-9@._/-]+$/.test(args.packageName)) {
2133
- 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);
2134
2178
  }
2135
2179
  const result = await npmViewPackage(args.packageName);
2136
2180
  return result;
2137
2181
  }
2138
2182
  catch (error) {
2139
- 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);
2140
2184
  }
2141
2185
  });
2142
2186
  }
@@ -2194,7 +2238,7 @@ async function npmViewPackage(packageName) {
2194
2238
  return createSuccessResult$1(viewResult);
2195
2239
  }
2196
2240
  catch (error) {
2197
- 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);
2198
2242
  }
2199
2243
  });
2200
2244
  }