octocode-mcp 2.3.2 → 2.3.4

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 +53 -20
  2. package/build/index.js +281 -156
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <div>
6
6
  <img src="./assets/logo.png">
7
7
 
8
- [![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](./package.json)
8
+ [![Version](https://img.shields.io/badge/version-2.3.2-blue.svg)](./package.json)
9
9
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](./package.json)
10
10
  [![MCP](https://img.shields.io/badge/MCP-Compatible-purple.svg)](https://modelcontextprotocol.io/)
11
11
  </div>
@@ -55,7 +55,7 @@ It's the tool you reach for when you need to understand *"how does this work?"*
55
55
 
56
56
  ### 1. Install Prerequisites
57
57
  ```bash
58
- # Install Node.js 21+
58
+ # Install Node.js 18.12+
59
59
  brew install node # macOS
60
60
  # or download from https://nodejs.org/
61
61
 
@@ -93,35 +93,44 @@ npm login
93
93
 
94
94
  **That's it!** No personal access tokens, no config files, no complex setup. Octocode leverages [GitHub CLI](https://cli.github.com/) authentication behind the scenes and **automatically works with your organization's private repositories**.
95
95
 
96
- ## Example Questions 💬
96
+ ![Installation Demo](assets/installation.gif)
97
97
 
98
- **Learning & Research:**
99
- - *"How do popular libraries implement rate limiting?"*
100
- - *"Show me Server Actions patterns in Next.js applications"*
101
- - *"What are the differences between Vue and React rendering?"*
102
98
 
103
- **Architecture & Patterns:**
104
- - *"How is authentication handled in enterprise applications?"*
105
- - *"Show me microservices communication patterns"*
106
- - *"Find examples of event-driven architecture implementations"*
99
+ ## How Octocode Works 🔄
107
100
 
108
- **Organization & Private Repositories:**
109
- - *"Show me authentication patterns used in our team's repositories"*
110
- - *"Find internal libraries and how they're implemented in our org"*
111
- - *"Analyze our company's coding standards and patterns"*
101
+ **Smart Discovery Flow:**
102
+ 1. **🔍 Query Analysis** AI determines the best search strategy based on your question
103
+ 2. **⚡ Multi-Tool Orchestration** Combines GitHub + NPM searches intelligently
104
+ 3. **🔄 Smart Fallbacks** Automatically retries with different approaches if initial search fails
105
+ 4. **🔗 Cross-Reference Discovery** → Links packages to repositories, finds related implementations
106
+ 5. **🎯 Context Synthesis** → Provides comprehensive understanding across multiple sources
112
107
 
113
- **Specific Code Analysis:**
114
- - *"How does lodash implement debouncing?"*
115
- - *"Show usage examples of this API: `createContext`"*
116
- - *"Find React hooks patterns for data fetching"*
108
+ ## Example Flows
109
+
110
+ ### Example 1: LangGraph Node.js Implementation Tutorial
111
+ **Query:** "Show implementations of langgraph in node js. Make a tutorial for how to implement a simple agent using OpenAI API."
112
+
113
+ ![LangGraph Node.js Tutorial](assets/langchainTutorial.gif)
114
+
115
+ ### Example 2: Zustand React State Management
116
+ **Query:** "Show me how to add zustand to react application. Show examples and best practices"
117
+
118
+ ![Zustand React State Management](assets/reactZustand.gif)
119
+
120
+ ### Example 3: React vs Vue.js Rendering Comparison
121
+ **Query:** "How did React implement their concurrent rendering flows? How is it different from Vue.js rendering mechanism? Which is better?"
122
+
123
+ ![React vs Vue.js Rendering Comparison](assets/reactVSVueJS.gif)
117
124
 
118
125
  ## Core Features 🛠️
119
126
 
120
127
  ### 🧠 AI-Powered Advanced Search
121
128
  - **Heuristic Pattern Recognition** - Finds relevant code even with vague or incomplete queries
122
- - **Smart Fallback Strategies** - Automatically tries alternative approaches when searches fail
129
+ - **Smart Fallback Strategies** - Automatically tries alternative approaches when searches fail with actionable suggestions
130
+ - **Boolean Search Intelligence** - Automatic query optimization with smart boolean operators (3-5x performance improvement)
123
131
  - **Context-Aware Discovery** - Understands code relationships and suggests related implementations
124
132
  - **Multi-Strategy Search** - Combines semantic, syntactic, and dependency-based search methods
133
+ - **Graceful Error Recovery** - Comprehensive error handling with intelligent retry mechanisms
125
134
 
126
135
  ### 🔗 Connection Intelligence
127
136
  - **Repository-Package Mapping** - Automatically links NPM packages to their GitHub repositories
@@ -148,6 +157,24 @@ npm login
148
157
  - **🔑 No Token Management** - Uses [GitHub CLI](https://cli.github.com/) authentication, no personal access tokens needed
149
158
  - **🛡️ Privacy by Design** - All API calls use your existing `gh` CLI permissions directly
150
159
 
160
+ ### Command Execution Security 🔒
161
+
162
+ **Robust protection against prompt injections and malicious command execution:**
163
+
164
+ - **⚪ Allowlisted Commands Only** - Only pre-approved, safe NPM and GitHub CLI commands are executable
165
+ - NPM: `view`, `search`, `ping`, `config`, `whoami`
166
+ - GitHub CLI: `search`, `api`, `auth`, `org`
167
+ - **🛡️ Argument Sanitization** - All command arguments are properly escaped to prevent shell injection attacks
168
+ - **✅ Pre-execution Validation** - Every command is validated against allowed lists before execution
169
+ - **🔧 Controlled Environment** - Commands run in a secure, cross-platform shell environment with controlled variables
170
+ - **Cross-platform shells**: Uses `/bin/sh` on Unix/macOS, `cmd.exe` or `powershell.exe` on Windows - minimal, standard shells
171
+ - **PowerShell support**: Modern Windows environments can optionally use PowerShell with enhanced security
172
+ - **Why minimal shells are safe**: Avoids user's potentially customized shells with aliases, functions, plugins, or advanced features
173
+ - **Controlled variables**: Only essential environment variables (`PATH`, `SHELL`) are passed, preventing environment-based attacks
174
+ - **Platform-specific escaping**: Uses appropriate argument escaping for each platform (single quotes on Unix, double quotes for CMD, single quotes for PowerShell)
175
+ - **🚫 No Arbitrary Execution** - System cannot execute arbitrary shell commands or scripts
176
+ - **⏱️ Timeout Protection** - All commands have execution timeouts to prevent resource exhaustion
177
+
151
178
  ## Best Practices 💡
152
179
 
153
180
  **AI-Powered Search Tips:**
@@ -193,6 +220,12 @@ npm whoami
193
220
  - **No additional setup** - If you have access to private repos through your organization, they work immediately
194
221
  - **Verify access** - Run `gh auth status` to see your organization memberships
195
222
 
223
+ **💻 Windows PowerShell Support:**
224
+ - **Modern shell support** - Optionally use PowerShell instead of cmd.exe on Windows
225
+ - **Enhanced security** - PowerShell provides better argument escaping and modern features
226
+ - **Automatic detection** - The system automatically detects Windows and applies appropriate shell configurations
227
+ - **Zero configuration** - Works seamlessly with existing setups, no additional configuration needed
228
+
196
229
  **Why GitHub CLI Authentication?**
197
230
  - ✅ **No token creation** - GitHub CLI handles OAuth flow automatically
198
231
  - ✅ **Enterprise compatible** - Works with SSO, SAML, and 2FA out of the box
package/build/index.js CHANGED
@@ -3,40 +3,44 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { exec } from 'child_process';
5
5
  import { promisify } from 'util';
6
+ import { platform } from 'os';
6
7
  import NodeCache from 'node-cache';
7
8
  import crypto from 'crypto';
8
9
  import z from 'zod';
9
10
 
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.
11
+ const PROMPT_SYSTEM_PROMPT = `Expert code research assistant for GitHub/NPM ecosystems (public/private). Use powerful semantic search for efficient code discovery.
12
12
 
13
- IMPORTANT: check users github organizations and use them in github search tools if needed
13
+ CORE TOOLS:
14
+ GitHub: Code, repos, issues, PRs, commits - supports boolean logic (AND/OR/NOT) and exact phrases
15
+ NPM: Package search (fuzzy only, no boolean) + metadata (repo URLs, exports, dependencies)
16
+ API Status: Check connectivity + user's GitHub organizations for private access
14
17
 
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)
18
+ SEARCH STRATEGIES:
19
+ GitHub Code/Issues/PRs/Commits:
20
+ OR (broad): "useState OR setState" - finds alternatives
21
+ AND (precise): "react AND hooks" - requires both terms
22
+ NOT (filter): "auth NOT test" - excludes noise
23
+ Quotes (exact): "useEffect cleanup" - literal phrases
24
+ Combine with filters: language, path, owner for focus
19
25
 
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
26
+ NPM Search:
27
+ Space-separated keywords only: "react state management"
28
+ No boolean operators supported
29
+ Use npm_view_package for direct package metadata GitHub repo URL
27
30
 
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
31
+ OPTIMIZATION:
32
+ Check user's GitHub orgs for private repo access
33
+ npm_view_package gives repo URL instantly - avoid GitHub repo search
34
+ Use targeted searches, avoid redundant tool calls
35
+ Combine tools strategically: NPM GitHub file content
36
+ Discovery: comprehensive multi-tool approach
37
+ Direct queries: quick targeted approach
34
38
 
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`;
39
+ CLI Help using CLI (use when needed to check failures)
40
+ gh <command> --help
41
+ npm <command> --help
42
+
43
+ Always provide code snippets and documentation references.`;
40
44
 
41
45
  function createResult(data, isError = false, suggestions) {
42
46
  const text = isError
@@ -133,6 +137,86 @@ function isValidNpmCommand(command) {
133
137
  function isValidGhCommand(command) {
134
138
  return ALLOWED_GH_COMMANDS.includes(command);
135
139
  }
140
+ /**
141
+ * Get platform-specific shell configuration with PowerShell support
142
+ */
143
+ function getShellConfig(preferredWindowsShell) {
144
+ const isWindows = platform() === 'win32';
145
+ if (!isWindows) {
146
+ return {
147
+ shell: '/bin/sh',
148
+ shellEnv: '/bin/sh',
149
+ type: 'unix',
150
+ };
151
+ }
152
+ // Windows shell selection
153
+ const usesPowerShell = preferredWindowsShell === 'powershell';
154
+ if (usesPowerShell) {
155
+ return {
156
+ shell: 'powershell.exe',
157
+ shellEnv: 'powershell.exe',
158
+ type: 'powershell',
159
+ };
160
+ }
161
+ return {
162
+ shell: 'cmd.exe',
163
+ shellEnv: 'cmd.exe',
164
+ type: 'cmd',
165
+ };
166
+ }
167
+ /**
168
+ * Escape shell arguments to prevent shell injection and handle special characters
169
+ * Cross-platform compatible escaping for Windows CMD, PowerShell, and Unix shells
170
+ */
171
+ function escapeShellArg(arg, shellType) {
172
+ // Auto-detect shell type if not provided
173
+ if (!shellType) {
174
+ const isWindows = platform() === 'win32';
175
+ shellType = isWindows ? 'cmd' : 'unix';
176
+ }
177
+ switch (shellType) {
178
+ case 'powershell':
179
+ return escapePowerShellArg(arg);
180
+ case 'cmd':
181
+ return escapeWindowsCmdArg(arg);
182
+ case 'unix':
183
+ default:
184
+ return escapeUnixShellArg(arg);
185
+ }
186
+ }
187
+ /**
188
+ * Escape arguments for PowerShell
189
+ * PowerShell uses single quotes for literal strings and has special escaping rules
190
+ */
191
+ function escapePowerShellArg(arg) {
192
+ // PowerShell special characters that need escaping
193
+ if (/[\s&<>|;`$@"'()[\]{}]/.test(arg)) {
194
+ // Use single quotes for literal strings in PowerShell
195
+ // Escape single quotes by doubling them
196
+ return `'${arg.replace(/'/g, "''")}'`;
197
+ }
198
+ return arg;
199
+ }
200
+ /**
201
+ * Escape arguments for Windows CMD
202
+ */
203
+ function escapeWindowsCmdArg(arg) {
204
+ // Windows CMD escaping
205
+ if (/[\s&<>|^"]/.test(arg)) {
206
+ return `"${arg.replace(/"/g, '""')}"`;
207
+ }
208
+ return arg;
209
+ }
210
+ /**
211
+ * Escape arguments for Unix shells (/bin/sh, bash, etc.)
212
+ */
213
+ function escapeUnixShellArg(arg) {
214
+ // Unix shell escaping
215
+ if (/[^\w\-._/:=@]/.test(arg)) {
216
+ return `'${arg.replace(/'/g, "'\"'\"'")}'`;
217
+ }
218
+ return arg;
219
+ }
136
220
  /**
137
221
  * Execute NPM commands safely by validating against allowed commands
138
222
  * Security: Only executes commands that start with "npm {ALLOWED_COMMAND}"
@@ -142,27 +226,22 @@ async function executeNpmCommand(command, args = [], options = {}) {
142
226
  if (!isValidNpmCommand(command)) {
143
227
  return createErrorResult('Command not registered', new Error(`NPM command '${command}' is not in the allowed list`));
144
228
  }
229
+ // Get shell configuration
230
+ const shellConfig = getShellConfig(options.windowsShell);
145
231
  // Build command with validated prefix and properly escaped arguments
146
- const escapedArgs = args.map(escapeShellArg);
232
+ const escapedArgs = args.map(arg => escapeShellArg(arg, shellConfig.type));
147
233
  const fullCommand = `npm ${command} ${escapedArgs.join(' ')}`;
148
- const executeNpmCommand = () => executeCommand(fullCommand, 'npm', options);
234
+ const executeNpmCommand = () => executeCommand(fullCommand, 'npm', options, shellConfig);
149
235
  if (options.cache) {
150
- const cacheKey = generateCacheKey('npm-exec', { command, args });
236
+ const cacheKey = generateCacheKey('npm-exec', {
237
+ command,
238
+ args,
239
+ shell: shellConfig.type,
240
+ });
151
241
  return withCache(cacheKey, executeNpmCommand);
152
242
  }
153
243
  return executeNpmCommand();
154
244
  }
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
- }
166
245
  /**
167
246
  * Execute GitHub CLI commands safely by validating against allowed commands
168
247
  * Security: Only executes commands that start with "gh {ALLOWED_COMMAND}"
@@ -172,12 +251,18 @@ async function executeGitHubCommand(command, args = [], options = {}) {
172
251
  if (!isValidGhCommand(command)) {
173
252
  return createErrorResult('Command not registered', new Error(`GitHub command '${command}' is not in the allowed list`));
174
253
  }
254
+ // Get shell configuration
255
+ const shellConfig = getShellConfig(options.windowsShell);
175
256
  // Build command with validated prefix and properly escaped arguments
176
- const escapedArgs = args.map(escapeShellArg);
257
+ const escapedArgs = args.map(arg => escapeShellArg(arg, shellConfig.type));
177
258
  const fullCommand = `gh ${command} ${escapedArgs.join(' ')}`;
178
- const executeGhCommand = () => executeCommand(fullCommand, 'github', options);
259
+ const executeGhCommand = () => executeCommand(fullCommand, 'github', options, shellConfig);
179
260
  if (options.cache) {
180
- const cacheKey = generateCacheKey('gh-exec', { command, args });
261
+ const cacheKey = generateCacheKey('gh-exec', {
262
+ command,
263
+ args,
264
+ shell: shellConfig.type,
265
+ });
181
266
  return withCache(cacheKey, executeGhCommand);
182
267
  }
183
268
  return executeGhCommand();
@@ -186,21 +271,23 @@ async function executeGitHubCommand(command, args = [], options = {}) {
186
271
  * Execute shell commands with timeout and error handling
187
272
  * Security: Should only be called with pre-validated command prefixes
188
273
  */
189
- async function executeCommand(fullCommand, type, options = {}) {
274
+ async function executeCommand(fullCommand, type, options = {}, shellConfig) {
190
275
  try {
191
276
  const defaultTimeout = type === 'npm' ? 30000 : 60000;
277
+ const config = shellConfig || getShellConfig(options.windowsShell);
192
278
  const execOptions = {
193
279
  timeout: options.timeout || defaultTimeout,
280
+ maxBuffer: 5 * 1024 * 1024, // 5MB buffer limit (increased from default 1MB)
194
281
  cwd: options.cwd,
195
282
  env: {
196
283
  ...process.env,
197
284
  ...options.env,
198
- // Ensure clean shell environment
199
- SHELL: '/bin/sh',
285
+ // Ensure clean shell environment - cross-platform compatible
286
+ SHELL: config.shellEnv,
200
287
  PATH: process.env.PATH,
201
288
  },
202
289
  encoding: 'utf-8',
203
- shell: '/bin/sh', // Use sh instead of default shell
290
+ shell: config.shell, // Use platform-appropriate shell
204
291
  };
205
292
  const { stdout, stderr } = await safeExecAsync(fullCommand, execOptions);
206
293
  // Handle different warning patterns for npm vs gh
@@ -220,6 +307,9 @@ async function executeCommand(fullCommand, type, options = {}) {
220
307
  result: stdout,
221
308
  timestamp: new Date().toISOString(),
222
309
  type,
310
+ platform: platform(),
311
+ shell: config.shell,
312
+ shellType: config.type,
223
313
  ...(stderr && { warning: stderr }), // Include warnings but don't treat as error
224
314
  });
225
315
  }
@@ -359,32 +449,16 @@ function registerApiStatusCheckTool(server) {
359
449
  }
360
450
 
361
451
  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
452
+ const DESCRIPTION$8 = `Search code across GitHub repositories using strategic boolean operators and filters with "gh code search" command.
384
453
 
385
- RESTRICTIVENESS SCALE: OR < AND < Exact Phrase (Broadest → Most Precise)
454
+ SEARCH PATTERNS:
455
+ OR (broad): "useState hook" → "useState OR hook" (auto-default for multi-word)
456
+ AND (precise): "react AND hooks" (both terms required)
457
+ NOT (filter): "auth NOT test" (exclude unwanted)
458
+ Exact phrases: "useState hook" (quoted for literal match)
459
+ Combine with filters: language, owner, path for laser focus
386
460
 
387
- COMBINE FILTERS: Mix query with language, owner, path filters for laser-focused results.`;
461
+ RESTRICTIVENESS: OR (broadest) < AND < Exact Phrase (most precise)`;
388
462
  function registerGitHubSearchCodeTool(server) {
389
463
  server.tool(TOOL_NAME$8, DESCRIPTION$8, {
390
464
  query: z
@@ -444,8 +518,10 @@ function registerGitHubSearchCodeTool(server) {
444
518
  openWorldHint: true,
445
519
  }, async (args) => {
446
520
  try {
447
- if (args.repo && !args.owner) {
448
- return createResult('Repository search requires owner parameter - specify owner when searching specific repositories', true);
521
+ // Validate parameter combinations
522
+ const validationError = validateSearchParameters(args);
523
+ if (validationError) {
524
+ return createResult(validationError, true);
449
525
  }
450
526
  const result = await searchGitHubCode(args);
451
527
  if (result.isError) {
@@ -469,7 +545,12 @@ function registerGitHubSearchCodeTool(server) {
469
545
  });
470
546
  }
471
547
  catch (error) {
472
- return createErrorResult$1('GitHub code search failed - check repository access or simplify query', error);
548
+ const errorMessage = error.message || '';
549
+ // Handle JSON parsing errors
550
+ if (errorMessage.includes('JSON')) {
551
+ return createErrorResult$1('GitHub CLI returned invalid response - check if GitHub CLI is up to date with "gh version" and try again', error);
552
+ }
553
+ return createErrorResult$1('GitHub code search failed - verify parameters and try with simpler query or specific filters (language, owner, path)', error);
473
554
  }
474
555
  });
475
556
  }
@@ -489,10 +570,12 @@ function parseSearchQuery(query, filters) {
489
570
  exactPhrases.push(match);
490
571
  processedQuery = processedQuery.replace(match, placeholder);
491
572
  });
492
- // Step 3: Smart boolean logic - default to OR between terms if no explicit operators
573
+ // Step 3: Check complexity BEFORE adding auto-OR logic
574
+ const originalHasComplexLogic = hasComplexBooleanLogic(processedQuery);
575
+ // Step 4: Smart boolean logic - default to OR between terms if no explicit operators
493
576
  let searchQuery = processedQuery;
494
577
  // Check if query already has explicit boolean operators
495
- if (!hasComplexBooleanLogic(processedQuery)) {
578
+ if (!originalHasComplexLogic) {
496
579
  // Split by whitespace and join with OR for better search results
497
580
  const terms = processedQuery
498
581
  .trim()
@@ -502,21 +585,22 @@ function parseSearchQuery(query, filters) {
502
585
  searchQuery = terms.join(' OR ');
503
586
  }
504
587
  }
505
- // Step 4: Add GitHub-specific filters that go in the query string
588
+ // Step 5: Handle filters differently based on ORIGINAL query complexity
506
589
  const githubFilters = [];
590
+ // Always add path and visibility to query string (they don't have CLI equivalents)
507
591
  if (filters.path) {
508
592
  githubFilters.push(`path:${filters.path}`);
509
593
  }
510
594
  if (filters.visibility) {
511
595
  githubFilters.push(`visibility:${filters.visibility}`);
512
596
  }
513
- // For complex boolean queries, add language/extension/filename/size to query string
514
- const hasComplexLogic = hasComplexBooleanLogic(searchQuery);
515
- if (hasComplexLogic) {
597
+ // For complex boolean queries, add ALL filters to query string to avoid CLI conflicts
598
+ if (originalHasComplexLogic) {
516
599
  if (filters.language) {
517
600
  githubFilters.push(`language:${filters.language}`);
518
601
  }
519
- if (filters.extension) {
602
+ // For complex queries with both language and extension, prioritize language
603
+ if (filters.extension && !filters.language) {
520
604
  githubFilters.push(`extension:${filters.extension}`);
521
605
  }
522
606
  if (filters.filename) {
@@ -526,11 +610,11 @@ function parseSearchQuery(query, filters) {
526
610
  githubFilters.push(`size:${filters.size}`);
527
611
  }
528
612
  }
529
- // Step 5: Combine query with GitHub filters
613
+ // Step 6: Combine query with GitHub filters using proper spacing
530
614
  if (githubFilters.length > 0) {
531
615
  searchQuery = `${searchQuery} ${githubFilters.join(' ')}`;
532
616
  }
533
- // Step 6: Restore exact phrases
617
+ // Step 7: Restore exact phrases
534
618
  exactPhrases.forEach((phrase, index) => {
535
619
  const placeholder = `__EXACT_PHRASE_${index}__`;
536
620
  searchQuery = searchQuery.replace(placeholder, phrase);
@@ -545,19 +629,17 @@ function hasComplexBooleanLogic(query) {
545
629
  return booleanOperators.test(query);
546
630
  }
547
631
  /**
548
- * Build command line arguments for GitHub CLI
632
+ * Build command line arguments for GitHub CLI with improved parameter handling
549
633
  */
550
634
  function buildGitHubCliArgs(params) {
551
635
  const args = ['code'];
552
636
  // Parse and add the main search query
553
637
  const searchQuery = parseSearchQuery(params.query, params);
554
638
  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
639
+ // Determine strategy based on ORIGINAL query complexity, not processed query
640
+ const hasComplexLogic = hasComplexBooleanLogic(params.query);
641
+ // For simple queries, use CLI flags for better performance and validation
642
+ if (!hasComplexLogic) {
561
643
  if (params.language) {
562
644
  args.push(`--language=${params.language}`);
563
645
  }
@@ -571,15 +653,16 @@ function buildGitHubCliArgs(params) {
571
653
  args.push(`--size=${params.size}`);
572
654
  }
573
655
  }
656
+ // For complex queries, filters are already in the query string (handled by parseSearchQuery)
657
+ // Always add limit
574
658
  if (params.limit) {
575
659
  args.push(`--limit=${params.limit}`);
576
660
  }
577
- // Handle match parameter - can be string or array
661
+ // Handle match parameter with conflict resolution
578
662
  if (params.match) {
579
663
  const matchValues = Array.isArray(params.match)
580
664
  ? params.match
581
665
  : [params.match];
582
- // GitHub API limitation: can't use both in:file and in:path in same query
583
666
  // Use the first match type when multiple are provided
584
667
  const matchValue = matchValues[0];
585
668
  args.push(`--match=${matchValue}`);
@@ -591,7 +674,7 @@ function buildGitHubCliArgs(params) {
591
674
  : [params.owner];
592
675
  ownerValues.forEach(owner => args.push(`--owner=${owner}`));
593
676
  }
594
- // Handle repository filters
677
+ // Handle repository filters with improved validation
595
678
  if (params.owner && params.repo) {
596
679
  const owners = Array.isArray(params.owner) ? params.owner : [params.owner];
597
680
  const repos = Array.isArray(params.repo) ? params.repo : [params.repo];
@@ -623,10 +706,65 @@ async function searchGitHubCode(params) {
623
706
  return result;
624
707
  }
625
708
  catch (error) {
626
- return createErrorResult$1('Code search command failed - verify GitHub CLI is authenticated', error);
709
+ const errorMessage = error.message || '';
710
+ // Parse specific GitHub CLI error types
711
+ if (errorMessage.includes('authentication')) {
712
+ return createErrorResult$1('GitHub CLI authentication required - run the api_status_check tool to verify authentication and available organizations', error);
713
+ }
714
+ if (errorMessage.includes('rate limit')) {
715
+ return createErrorResult$1('GitHub API rate limit exceeded - wait a few minutes before searching again or use more specific filters to reduce results', error);
716
+ }
717
+ if (errorMessage.includes('validation failed') ||
718
+ errorMessage.includes('Invalid query')) {
719
+ return createErrorResult$1('Invalid search query syntax - check boolean operators (AND/OR/NOT), quotes for exact phrases, and filter formats. Try simplifying your query', error);
720
+ }
721
+ if (errorMessage.includes('repository not found') ||
722
+ errorMessage.includes('owner not found')) {
723
+ return createErrorResult$1('Repository or owner not found - run api_status_check tool to verify available organizations and access permissions', error);
724
+ }
725
+ if (errorMessage.includes('timeout')) {
726
+ return createErrorResult$1('Search timeout - query too broad or complex. Try adding filters like language, owner, or path to narrow results', error);
727
+ }
728
+ // Generic fallback with helpful guidance
729
+ return createErrorResult$1('GitHub code search failed - run api_status_check tool to verify authentication and permissions, or try simplifying your query', error);
627
730
  }
628
731
  });
629
732
  }
733
+ /**
734
+ * Validate parameter combinations to prevent conflicts
735
+ */
736
+ function validateSearchParameters(params) {
737
+ // Query validation
738
+ if (!params.query.trim()) {
739
+ return 'Empty search query - provide a search term like "useState", "function init", or "api AND endpoint"';
740
+ }
741
+ if (params.query.length > 1000) {
742
+ return 'Search query too long - limit to 1000 characters. Try breaking into smaller, focused searches';
743
+ }
744
+ // Repository validation
745
+ if (params.repo && !params.owner) {
746
+ return 'Missing owner parameter - when searching specific repositories, format as owner/repo (e.g., "microsoft/vscode") or provide both owner and repo parameters';
747
+ }
748
+ // Invalid characters in query
749
+ if (params.query.includes('\\') && !params.query.includes('\\"')) {
750
+ return 'Invalid escape characters in query - use quotes for exact phrases: "exact phrase" instead of escaping';
751
+ }
752
+ // Boolean operator validation
753
+ const invalidBooleans = params.query.match(/\b(and|or|not)\b/g);
754
+ if (invalidBooleans) {
755
+ return `Boolean operators must be uppercase - use ${invalidBooleans.map(op => op.toUpperCase()).join(', ')} instead of ${invalidBooleans.join(', ')}`;
756
+ }
757
+ // Unmatched quotes
758
+ const quoteCount = (params.query.match(/"/g) || []).length;
759
+ if (quoteCount % 2 !== 0) {
760
+ return 'Unmatched quotes in query - ensure all quotes are properly paired for exact phrase matching';
761
+ }
762
+ // Size parameter validation
763
+ if (params.size && !/^[<>]=?\d+$|^\d+\.\.\d+$|^\d+$/.test(params.size)) {
764
+ return 'Invalid size format - use ">100", "<50", "10..100", or "100" for file size filtering';
765
+ }
766
+ return null; // No validation errors
767
+ }
630
768
 
631
769
  const TOOL_NAME$7 = 'github_get_file_content';
632
770
  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.`;
@@ -729,6 +867,13 @@ async function fetchGitHubFileContent(params) {
729
867
  else if (errorMsg.includes('403')) {
730
868
  return createErrorResult$1('Access denied - repository may be private or require authentication', new Error(`${owner}/${repo}`));
731
869
  }
870
+ else if (errorMsg.includes('maxBuffer') ||
871
+ errorMsg.includes('stdout maxBuffer length exceeded')) {
872
+ return createErrorResult$1(` File too large to fetch (buffer limit exceeded)\n\n` +
873
+ `This usually happens with files >300KB. Consider:\n` +
874
+ ` Use github_search_code to find specific patterns\n` +
875
+ ` Browse directory structure with github_get_contents`, new Error(`Buffer overflow: ${filePath}`));
876
+ }
732
877
  else {
733
878
  return createErrorResult$1('Fetch failed - check repository and file path', new Error(errorMsg));
734
879
  }
@@ -736,6 +881,15 @@ async function fetchGitHubFileContent(params) {
736
881
  return await processFileContent(result, owner, repo, branch, filePath);
737
882
  }
738
883
  catch (error) {
884
+ const errorMessage = error.message;
885
+ // Handle maxBuffer errors that escape the main try-catch
886
+ if (errorMessage.includes('maxBuffer') ||
887
+ errorMessage.includes('stdout maxBuffer length exceeded')) {
888
+ return createErrorResult$1(` File too large to fetch (buffer limit exceeded)\n\n` +
889
+ `This usually happens with files >300KB. Consider:\n` +
890
+ ` Use github_search_code to find specific patterns instead of fetching the whole file\n` +
891
+ ` Browse directory structure with github_get_contents`, error);
892
+ }
739
893
  return createErrorResult$1('Unexpected error during file fetch - check connection and permissions', error);
740
894
  }
741
895
  });
@@ -749,10 +903,15 @@ async function processFileContent(result, owner, repo, branch, filePath) {
749
903
  return createErrorResult$1('Path is directory - use github_get_contents to browse directory structure', new Error(filePath));
750
904
  }
751
905
  const fileSize = fileData.size || 0;
752
- const MAX_FILE_SIZE = 500 * 1024; // 500KB limit for simplicity
753
- // Check file size
906
+ const MAX_FILE_SIZE = 300 * 1024; // 300KB limit for better performance and reliability
907
+ // Check file size with helpful message
754
908
  if (fileSize > MAX_FILE_SIZE) {
755
- return createErrorResult$1('File too large - files over 500KB cannot be fetched', new Error(`${Math.round(fileSize / 1024)}KB > 500KB`));
909
+ const fileSizeKB = Math.round(fileSize / 1024);
910
+ const maxSizeKB = Math.round(MAX_FILE_SIZE / 1024);
911
+ return createErrorResult$1(` File too large to display (${fileSizeKB}KB > ${maxSizeKB}KB limit)\n\n` +
912
+ `For large files like this, consider:\n` +
913
+ ` Use github_get_contents to browse directory structure\n` +
914
+ ` Search specific code patterns with github_search_code`, new Error(`File: ${filePath} (${fileSizeKB}KB)`));
756
915
  }
757
916
  // Get and decode content
758
917
  const base64Content = fileData.content?.replace(/\s/g, ''); // Remove all whitespace
@@ -1011,44 +1170,16 @@ async function searchGitHubRepos(params) {
1011
1170
  function buildGitHubReposSearchCommand(params) {
1012
1171
  // Build query following GitHub CLI patterns
1013
1172
  const query = params.query?.trim() || '';
1014
- // Handle complex queries (with qualifiers, operators, or --) differently
1015
- const hasComplexSyntax = query.includes('--') ||
1016
- query.includes(':') ||
1017
- query.includes('OR') ||
1018
- query.includes('AND') ||
1019
- query.includes('(') ||
1020
- query.includes(')') ||
1021
- query.startsWith('-');
1022
1173
  const args = ['repos'];
1023
- // Only add query if it exists
1174
+ // Only add query if it exists and handle it properly
1024
1175
  if (query) {
1025
- if (hasComplexSyntax) {
1026
- // For complex queries with special syntax, we need to be more careful
1027
- // Split by spaces but preserve quoted strings and handle special characters
1028
- const queryParts = query.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
1029
- queryParts.forEach(part => {
1030
- // If part contains shell special characters, quote it
1031
- if (/[><=&|$`(){}[\];\\]/.test(part) && !part.includes('"')) {
1032
- args.push(`"${part}"`);
1033
- }
1034
- else {
1035
- args.push(part);
1036
- }
1037
- });
1176
+ // For repository search, treat multi-word queries as a single quoted string
1177
+ // This matches GitHub CLI expected behavior for repo searches
1178
+ if (query.includes(' ')) {
1179
+ args.push(query); // Let GitHub CLI handle the quoting
1038
1180
  }
1039
1181
  else {
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
- });
1182
+ args.push(query);
1052
1183
  }
1053
1184
  }
1054
1185
  // Add JSON output with specific fields for structured data parsing
@@ -1077,14 +1208,15 @@ function buildGitHubReposSearchCommand(params) {
1077
1208
  const starsValue = params.stars.trim();
1078
1209
  const isValidStars = /^(\d+|>\d+|<\d+|\d+\.\.\d+|>=\d+|<=\d+)$/.test(starsValue);
1079
1210
  if (isValidStars) {
1080
- args.push(`--stars=${params.stars}`);
1211
+ // Don't add quotes around the stars value - GitHub CLI handles this internally
1212
+ args.push(`--stars=${starsValue}`);
1081
1213
  }
1082
1214
  }
1083
1215
  // SECONDARY FILTERS - only add if we have primary filters
1084
1216
  if (params.archived !== undefined)
1085
1217
  args.push(`--archived=${params.archived}`);
1086
1218
  if (params.created)
1087
- args.push(`--created="${params.created}"`);
1219
+ args.push(`--created=${params.created}`);
1088
1220
  if (params.includeForks)
1089
1221
  args.push(`--include-forks=${params.includeForks}`);
1090
1222
  if (params.license && params.license.length > 0)
@@ -1092,7 +1224,7 @@ function buildGitHubReposSearchCommand(params) {
1092
1224
  if (params.match)
1093
1225
  args.push(`--match=${params.match}`);
1094
1226
  if (params.updated)
1095
- args.push(`--updated="${params.updated}"`);
1227
+ args.push(`--updated=${params.updated}`);
1096
1228
  if (params.visibility)
1097
1229
  args.push(`--visibility=${params.visibility}`);
1098
1230
  if (params.goodFirstIssues)
@@ -1391,15 +1523,13 @@ function buildGitHubCommitsSearchCommand(params) {
1391
1523
 
1392
1524
  // TODO: add PR commeents. e.g, gh pr view <PR_NUMBER_OR_URL_OR_BRANCH> --comments
1393
1525
  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.
1526
+ const DESCRIPTION$4 = `Find pull requests and implementations with detailed metadata. Discover feature implementations, code review patterns, and development workflows.
1395
1527
 
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.`;
1528
+ SEARCH PATTERNS:
1529
+ Boolean: "fix AND bug", "refactor OR cleanup", "feature NOT draft"
1530
+ Exact phrases: "initial commit" (quoted)
1531
+ GitHub qualifiers: "is:merged review:approved base:main"
1532
+ Combine with filters for targeted PR discovery`;
1403
1533
  function registerSearchGitHubPullRequestsTool(server) {
1404
1534
  server.tool(TOOL_NAME$4, DESCRIPTION$4, {
1405
1535
  query: z
@@ -1600,9 +1730,9 @@ const TOOL_NAME$3 = 'npm_package_search';
1600
1730
  const DESCRIPTION$3 = `Search npm packages by keywords using fuzzy matching.
1601
1731
 
1602
1732
  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"
1733
+ NO BOOLEAN OPERATORS: NPM search does NOT support AND/OR/NOT - use space-separated keywords for broader search
1734
+ FUZZY MATCHING ONLY: No exact phrase matching - searches are approximate keyword matching
1735
+ KEYWORD-BASED: Best results with simple, space-separated terms like "react hooks" or "cli typescript"
1606
1736
 
1607
1737
  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.`;
1608
1738
  const MAX_DESCRIPTION_LENGTH = 100;
@@ -1930,15 +2060,13 @@ async function getSmartBranchFallback(owner, repo, requestedBranch) {
1930
2060
  }
1931
2061
 
1932
2062
  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
2063
+ const DESCRIPTION$1 = `Find GitHub issues with rich metadata (labels, reactions, comments, state). Discover pain points, feature requests, and bug patterns with boolean logic and GitHub qualifiers.
1940
2064
 
1941
- Filter by state, labels, assignee, or date ranges for comprehensive issue discovery.`;
2065
+ SEARCH PATTERNS:
2066
+ Boolean: "bug AND crash", "feature OR enhancement", "error NOT test"
2067
+ Exact phrases: "memory leak" (quoted)
2068
+ GitHub qualifiers: "is:open label:bug author:username"
2069
+ Combine with filters for precision`;
1942
2070
  function registerSearchGitHubIssuesTool(server) {
1943
2071
  server.tool(TOOL_NAME$1, DESCRIPTION$1, {
1944
2072
  query: z
@@ -2150,10 +2278,7 @@ function buildGitHubIssuesAPICommand(params) {
2150
2278
  }
2151
2279
 
2152
2280
  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.`;
2281
+ const DESCRIPTION = `Get comprehensive NPM package metadata efficiently. Returns repository URL, exports, dependencies, and version history without needing GitHub searches. Essential for finding package source code and understanding project structure.`;
2157
2282
  function registerNpmViewPackageTool(server) {
2158
2283
  server.tool(TOOL_NAME, DESCRIPTION, {
2159
2284
  packageName: z
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octocode-mcp",
3
- "version": "2.3.2",
3
+ "version": "2.3.4",
4
4
  "description": "Model Context Protocol (MCP) server for advanced GitHub repository analysis, code discovery, and npm package exploration. Provides AI assistants with powerful tools to search, analyze, and understand codebases across GitHub and npm ecosystems.",
5
5
  "author": "Guy Bary <guybary@gmail.com>",
6
6
  "homepage": "https://github.com/bgauryy/octocode-mcp#readme",