gemini-bridge 1.0.4__tar.gz → 1.0.5__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemini-bridge
3
- Version: 1.0.4
3
+ Version: 1.0.5
4
4
  Summary: Lightweight MCP server bridging Claude Code to Google's Gemini AI via official CLI
5
5
  Author-email: Shelakh <info@shelakh.com>
6
6
  License-Expression: MIT
@@ -537,83 +537,10 @@ gemini --version
537
537
  - Ensure Claude Code MCP configuration is correct
538
538
  - Check that the `gemini` command is in your PATH
539
539
 
540
- ### Timeout Issues 🔥
541
-
542
- Gemini Bridge now includes comprehensive timeout debugging. If you're experiencing timeout issues:
543
-
544
- **1. Use the Debug Tool**
545
- ```python
546
- # Get detailed diagnostics about your configuration
547
- get_debug_info()
548
- ```
549
-
550
- This will show:
551
- - Current timeout configuration
552
- - Gemini CLI status and version
553
- - Authentication status
554
- - Environment variables
555
- - System information
556
-
557
- **2. Configure Appropriate Timeout**
558
-
559
- For different operation types, recommended timeouts:
560
-
561
- - **Default operations**: 60 seconds (default)
562
- - **Large file analysis**: 240 seconds
563
- - **Complex multi-file operations**: 300+ seconds
564
-
565
- **Configuration Examples:**
566
-
567
- ```bash
568
- # Claude Code with 4-minute timeout for large operations
569
- claude mcp add gemini-bridge -s user --env GEMINI_BRIDGE_TIMEOUT=240 -- uvx gemini-bridge
570
-
571
- # Environment variable (if running manually)
572
- export GEMINI_BRIDGE_TIMEOUT=240
573
- ```
574
-
575
- **3. Common Timeout Scenarios**
576
-
577
- | Scenario | Recommended Timeout | Reason |
578
- |----------|-------------------|---------|
579
- | Single file < 10KB | 60s (default) | Fast processing |
580
- | Multiple files or large files | 240s | More content to process |
581
- | Complex code analysis | 300s | Deep reasoning required |
582
- | Very large files (>100KB) | 300-600s | Processing overhead |
583
-
584
- **4. Debugging Steps**
585
-
586
- 1. **Check your configuration**:
587
- ```bash
588
- # Run the debug tool to see current timeout
589
- # Look for "Actual timeout used" in the output
590
- ```
591
-
592
- 2. **Monitor logs**: The server now logs detailed timing information
593
-
594
- 3. **Test with smaller queries**: If large queries timeout, break them into smaller parts
595
-
596
- 4. **Verify Gemini CLI performance**: Test `gemini` directly with similar queries
597
-
598
- **5. Advanced Troubleshooting**
599
-
600
- If timeouts persist even with high timeout values:
601
-
602
- - **Network issues**: Check your internet connection
603
- - **Gemini CLI version**: Update with `npm install -g @google/gemini-cli@latest`
604
- - **Authentication**: Re-authenticate with `gemini auth login`
605
- - **System resources**: Check if your system is under high load
606
- - **File encoding**: Ensure files are UTF-8 encoded
607
- - **MCP client timeout**: Some clients have their own timeout settings
608
-
609
540
  ### Common Error Messages
610
541
  - **"CLI not available"**: Gemini CLI is not installed or not in PATH
611
542
  - **"Authentication required"**: Run `gemini auth login`
612
- - **"Timeout after X seconds"**: Operation took longer than configured timeout
613
- - Solution: Increase `GEMINI_BRIDGE_TIMEOUT` environment variable
614
- - For immediate testing: Use smaller files or simpler queries
615
- - **"Large content size warning"**: Files total >100KB, may need longer timeout
616
- - **"Very high timeout configured"**: Timeout >300s, failed operations will wait long
543
+ - **"Timeout after 60 seconds"**: Query took too long, try breaking it into smaller parts
617
544
 
618
545
  ## 🤝 Contributing
619
546
 
@@ -513,83 +513,10 @@ gemini --version
513
513
  - Ensure Claude Code MCP configuration is correct
514
514
  - Check that the `gemini` command is in your PATH
515
515
 
516
- ### Timeout Issues 🔥
517
-
518
- Gemini Bridge now includes comprehensive timeout debugging. If you're experiencing timeout issues:
519
-
520
- **1. Use the Debug Tool**
521
- ```python
522
- # Get detailed diagnostics about your configuration
523
- get_debug_info()
524
- ```
525
-
526
- This will show:
527
- - Current timeout configuration
528
- - Gemini CLI status and version
529
- - Authentication status
530
- - Environment variables
531
- - System information
532
-
533
- **2. Configure Appropriate Timeout**
534
-
535
- For different operation types, recommended timeouts:
536
-
537
- - **Default operations**: 60 seconds (default)
538
- - **Large file analysis**: 240 seconds
539
- - **Complex multi-file operations**: 300+ seconds
540
-
541
- **Configuration Examples:**
542
-
543
- ```bash
544
- # Claude Code with 4-minute timeout for large operations
545
- claude mcp add gemini-bridge -s user --env GEMINI_BRIDGE_TIMEOUT=240 -- uvx gemini-bridge
546
-
547
- # Environment variable (if running manually)
548
- export GEMINI_BRIDGE_TIMEOUT=240
549
- ```
550
-
551
- **3. Common Timeout Scenarios**
552
-
553
- | Scenario | Recommended Timeout | Reason |
554
- |----------|-------------------|---------|
555
- | Single file < 10KB | 60s (default) | Fast processing |
556
- | Multiple files or large files | 240s | More content to process |
557
- | Complex code analysis | 300s | Deep reasoning required |
558
- | Very large files (>100KB) | 300-600s | Processing overhead |
559
-
560
- **4. Debugging Steps**
561
-
562
- 1. **Check your configuration**:
563
- ```bash
564
- # Run the debug tool to see current timeout
565
- # Look for "Actual timeout used" in the output
566
- ```
567
-
568
- 2. **Monitor logs**: The server now logs detailed timing information
569
-
570
- 3. **Test with smaller queries**: If large queries timeout, break them into smaller parts
571
-
572
- 4. **Verify Gemini CLI performance**: Test `gemini` directly with similar queries
573
-
574
- **5. Advanced Troubleshooting**
575
-
576
- If timeouts persist even with high timeout values:
577
-
578
- - **Network issues**: Check your internet connection
579
- - **Gemini CLI version**: Update with `npm install -g @google/gemini-cli@latest`
580
- - **Authentication**: Re-authenticate with `gemini auth login`
581
- - **System resources**: Check if your system is under high load
582
- - **File encoding**: Ensure files are UTF-8 encoded
583
- - **MCP client timeout**: Some clients have their own timeout settings
584
-
585
516
  ### Common Error Messages
586
517
  - **"CLI not available"**: Gemini CLI is not installed or not in PATH
587
518
  - **"Authentication required"**: Run `gemini auth login`
588
- - **"Timeout after X seconds"**: Operation took longer than configured timeout
589
- - Solution: Increase `GEMINI_BRIDGE_TIMEOUT` environment variable
590
- - For immediate testing: Use smaller files or simpler queries
591
- - **"Large content size warning"**: Files total >100KB, may need longer timeout
592
- - **"Very high timeout configured"**: Timeout >300s, failed operations will wait long
519
+ - **"Timeout after 60 seconds"**: Query took too long, try breaking it into smaller parts
593
520
 
594
521
  ## 🤝 Contributing
595
522
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemini-bridge
3
- Version: 1.0.4
3
+ Version: 1.0.5
4
4
  Summary: Lightweight MCP server bridging Claude Code to Google's Gemini AI via official CLI
5
5
  Author-email: Shelakh <info@shelakh.com>
6
6
  License-Expression: MIT
@@ -537,83 +537,10 @@ gemini --version
537
537
  - Ensure Claude Code MCP configuration is correct
538
538
  - Check that the `gemini` command is in your PATH
539
539
 
540
- ### Timeout Issues 🔥
541
-
542
- Gemini Bridge now includes comprehensive timeout debugging. If you're experiencing timeout issues:
543
-
544
- **1. Use the Debug Tool**
545
- ```python
546
- # Get detailed diagnostics about your configuration
547
- get_debug_info()
548
- ```
549
-
550
- This will show:
551
- - Current timeout configuration
552
- - Gemini CLI status and version
553
- - Authentication status
554
- - Environment variables
555
- - System information
556
-
557
- **2. Configure Appropriate Timeout**
558
-
559
- For different operation types, recommended timeouts:
560
-
561
- - **Default operations**: 60 seconds (default)
562
- - **Large file analysis**: 240 seconds
563
- - **Complex multi-file operations**: 300+ seconds
564
-
565
- **Configuration Examples:**
566
-
567
- ```bash
568
- # Claude Code with 4-minute timeout for large operations
569
- claude mcp add gemini-bridge -s user --env GEMINI_BRIDGE_TIMEOUT=240 -- uvx gemini-bridge
570
-
571
- # Environment variable (if running manually)
572
- export GEMINI_BRIDGE_TIMEOUT=240
573
- ```
574
-
575
- **3. Common Timeout Scenarios**
576
-
577
- | Scenario | Recommended Timeout | Reason |
578
- |----------|-------------------|---------|
579
- | Single file < 10KB | 60s (default) | Fast processing |
580
- | Multiple files or large files | 240s | More content to process |
581
- | Complex code analysis | 300s | Deep reasoning required |
582
- | Very large files (>100KB) | 300-600s | Processing overhead |
583
-
584
- **4. Debugging Steps**
585
-
586
- 1. **Check your configuration**:
587
- ```bash
588
- # Run the debug tool to see current timeout
589
- # Look for "Actual timeout used" in the output
590
- ```
591
-
592
- 2. **Monitor logs**: The server now logs detailed timing information
593
-
594
- 3. **Test with smaller queries**: If large queries timeout, break them into smaller parts
595
-
596
- 4. **Verify Gemini CLI performance**: Test `gemini` directly with similar queries
597
-
598
- **5. Advanced Troubleshooting**
599
-
600
- If timeouts persist even with high timeout values:
601
-
602
- - **Network issues**: Check your internet connection
603
- - **Gemini CLI version**: Update with `npm install -g @google/gemini-cli@latest`
604
- - **Authentication**: Re-authenticate with `gemini auth login`
605
- - **System resources**: Check if your system is under high load
606
- - **File encoding**: Ensure files are UTF-8 encoded
607
- - **MCP client timeout**: Some clients have their own timeout settings
608
-
609
540
  ### Common Error Messages
610
541
  - **"CLI not available"**: Gemini CLI is not installed or not in PATH
611
542
  - **"Authentication required"**: Run `gemini auth login`
612
- - **"Timeout after X seconds"**: Operation took longer than configured timeout
613
- - Solution: Increase `GEMINI_BRIDGE_TIMEOUT` environment variable
614
- - For immediate testing: Use smaller files or simpler queries
615
- - **"Large content size warning"**: Files total >100KB, may need longer timeout
616
- - **"Very high timeout configured"**: Timeout >300s, failed operations will wait long
543
+ - **"Timeout after 60 seconds"**: Query took too long, try breaking it into smaller parts
617
544
 
618
545
  ## 🤝 Contributing
619
546
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gemini-bridge"
7
- version = "1.0.4"
7
+ version = "1.0.5"
8
8
  description = "Lightweight MCP server bridging Claude Code to Google's Gemini AI via official CLI"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -5,5 +5,5 @@ Version 1.0.0 - Production ready, radically simplified.
5
5
 
6
6
  from .mcp_server import main
7
7
 
8
- __version__ = "1.0.4"
8
+ __version__ = "1.0.5"
9
9
  __all__ = ["main"]
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Gemini MCP Server - Simple CLI Bridge
4
+ Version 1.0.5
5
+ A minimal MCP server to interface with Gemini AI via the gemini CLI.
6
+ Created by @shelakh/elyin
7
+ """
8
+
9
+ import logging
10
+ import os
11
+ import shutil
12
+ import subprocess
13
+ from typing import List, Optional
14
+
15
+ from mcp.server.fastmcp import FastMCP
16
+
17
+ mcp = FastMCP("gemini-assistant")
18
+
19
+
20
+ def _normalize_model_name(model: Optional[str]) -> str:
21
+ """
22
+ Normalize user-provided model identifiers to canonical Gemini CLI model names.
23
+ Defaults to gemini-2.5-flash when not provided or unrecognized.
24
+
25
+ Accepted forms:
26
+ - "flash", "2.5-flash", "gemini-2.5-flash"
27
+ - "pro", "2.5-pro", "gemini-2.5-pro"
28
+ """
29
+ if not model:
30
+ return "gemini-2.5-flash"
31
+ value = model.strip().lower()
32
+ # Common short aliases
33
+ if value in {"flash", "2.5-flash", "gemini-2.5-flash"}:
34
+ return "gemini-2.5-flash"
35
+ if value in {"pro", "2.5-pro", "gemini-2.5-pro"}:
36
+ return "gemini-2.5-pro"
37
+ # If the caller passed a full model name, keep it
38
+ if value.startswith("gemini-"):
39
+ return value
40
+ # Fallback to flash for anything else
41
+ return "gemini-2.5-flash"
42
+
43
+
44
+ def _get_timeout() -> int:
45
+ """
46
+ Get the timeout value from environment variable GEMINI_BRIDGE_TIMEOUT.
47
+ Defaults to 60 seconds if not set or invalid.
48
+
49
+ Returns:
50
+ Timeout value in seconds (positive integer)
51
+ """
52
+ timeout_str = os.getenv("GEMINI_BRIDGE_TIMEOUT")
53
+ if not timeout_str:
54
+ return 60
55
+
56
+ try:
57
+ timeout = int(timeout_str)
58
+ if timeout <= 0:
59
+ logging.warning("Invalid GEMINI_BRIDGE_TIMEOUT value '%s' (must be positive). Using default 60 seconds.", timeout_str)
60
+ return 60
61
+ return timeout
62
+ except ValueError:
63
+ logging.warning("Invalid GEMINI_BRIDGE_TIMEOUT value '%s' (must be integer). Using default 60 seconds.", timeout_str)
64
+ return 60
65
+
66
+
67
+ def execute_gemini_simple(query: str, directory: str = ".", model: Optional[str] = None) -> str:
68
+ """
69
+ Execute gemini CLI command for simple queries without file attachments.
70
+
71
+ Args:
72
+ query: The prompt to send to Gemini
73
+ directory: Working directory for the command
74
+ model: Optional model name (flash, pro, etc.)
75
+
76
+ Returns:
77
+ CLI output or error message
78
+ """
79
+ # Check if gemini CLI is available
80
+ if not shutil.which("gemini"):
81
+ return "Error: Gemini CLI not found. Install with: npm install -g @google/gemini-cli"
82
+
83
+ # Validate directory
84
+ if not os.path.isdir(directory):
85
+ return f"Error: Directory does not exist: {directory}"
86
+
87
+ # Build command - use stdin for input to avoid hanging
88
+ selected_model = _normalize_model_name(model)
89
+ cmd = ["gemini", "-m", selected_model]
90
+
91
+ # Execute CLI command - simple timeout, no retries
92
+ timeout = _get_timeout()
93
+ try:
94
+ result = subprocess.run(
95
+ cmd,
96
+ cwd=directory,
97
+ capture_output=True,
98
+ text=True,
99
+ timeout=timeout,
100
+ input=query
101
+ )
102
+
103
+ if result.returncode == 0:
104
+ return result.stdout.strip() if result.stdout.strip() else "No output from Gemini CLI"
105
+ else:
106
+ return f"Gemini CLI Error: {result.stderr.strip()}"
107
+
108
+ except subprocess.TimeoutExpired:
109
+ return f"Error: Gemini CLI command timed out after {timeout} seconds"
110
+ except Exception as e:
111
+ return f"Error executing Gemini CLI: {str(e)}"
112
+
113
+
114
+ def execute_gemini_with_files(query: str, directory: str = ".", files: Optional[List[str]] = None, model: Optional[str] = None) -> str:
115
+ """
116
+ Execute gemini CLI command with file attachments.
117
+
118
+ Args:
119
+ query: The prompt to send to Gemini
120
+ directory: Working directory for the command
121
+ files: List of file paths to attach (relative to directory)
122
+ model: Optional model name (flash, pro, etc.)
123
+
124
+ Returns:
125
+ CLI output or error message
126
+ """
127
+ # Check if gemini CLI is available
128
+ if not shutil.which("gemini"):
129
+ return "Error: Gemini CLI not found. Install with: npm install -g @google/gemini-cli"
130
+
131
+ # Validate directory
132
+ if not os.path.isdir(directory):
133
+ return f"Error: Directory does not exist: {directory}"
134
+
135
+ # Validate files parameter
136
+ if not files:
137
+ return "Error: No files provided for file attachment mode"
138
+
139
+ # Build command - use stdin for input to avoid hanging
140
+ selected_model = _normalize_model_name(model)
141
+ cmd = ["gemini", "-m", selected_model]
142
+
143
+ # Read and concatenate file contents
144
+ file_contents = []
145
+ for file_path in files:
146
+ try:
147
+ # Convert relative paths to absolute based on directory
148
+ if not os.path.isabs(file_path):
149
+ file_path = os.path.join(directory, file_path)
150
+
151
+ if os.path.isfile(file_path):
152
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
153
+ content = f.read()
154
+ file_contents.append(f"=== {os.path.basename(file_path)} ===\n{content}")
155
+ else:
156
+ file_contents.append(f"=== {os.path.basename(file_path)} ===\n[File not found]")
157
+ except Exception as e:
158
+ file_contents.append(f"=== {os.path.basename(file_path)} ===\n[Error reading file: {str(e)}]")
159
+
160
+ # Combine file contents with query
161
+ stdin_content = "\n\n".join(file_contents) + "\n\n" + query
162
+
163
+ # Execute CLI command - simple timeout, no retries
164
+ timeout = _get_timeout()
165
+ try:
166
+ result = subprocess.run(
167
+ cmd,
168
+ cwd=directory,
169
+ capture_output=True,
170
+ text=True,
171
+ timeout=timeout,
172
+ input=stdin_content
173
+ )
174
+
175
+ if result.returncode == 0:
176
+ return result.stdout.strip() if result.stdout.strip() else "No output from Gemini CLI"
177
+ else:
178
+ return f"Gemini CLI Error: {result.stderr.strip()}"
179
+
180
+ except subprocess.TimeoutExpired:
181
+ return f"Error: Gemini CLI command timed out after {timeout} seconds"
182
+ except Exception as e:
183
+ return f"Error executing Gemini CLI: {str(e)}"
184
+
185
+
186
+ @mcp.tool()
187
+ def consult_gemini(
188
+ query: str,
189
+ directory: str,
190
+ model: Optional[str] = None
191
+ ) -> str:
192
+ """
193
+ Send a query directly to Gemini CLI.
194
+
195
+ This is the core function - a direct bridge between Claude and Gemini.
196
+ No caching, no sessions, no complexity. Just execute and return.
197
+
198
+ Args:
199
+ query: The question or prompt to send to Gemini
200
+ directory: Working directory (required)
201
+ model: Optional model name (flash, pro, etc.)
202
+
203
+ Returns:
204
+ Gemini's response
205
+ """
206
+ return execute_gemini_simple(query, directory, model)
207
+
208
+
209
+ @mcp.tool()
210
+ def consult_gemini_with_files(
211
+ query: str,
212
+ directory: str,
213
+ files: Optional[List[str]] = None,
214
+ model: Optional[str] = None
215
+ ) -> str:
216
+ """
217
+ Send a query to Gemini CLI with file attachments.
218
+
219
+ Files are read and concatenated into the prompt. Simple and direct.
220
+
221
+ Args:
222
+ query: The question or prompt to send to Gemini
223
+ directory: Working directory (required)
224
+ files: List of file paths to attach (relative to directory)
225
+ model: Optional model name (flash, pro, etc.)
226
+
227
+ Returns:
228
+ Gemini's response with file context
229
+ """
230
+ if not files:
231
+ return "Error: files parameter is required for consult_gemini_with_files"
232
+ return execute_gemini_with_files(query, directory, files, model)
233
+
234
+
235
+ def main():
236
+ """Entry point for the MCP server."""
237
+ mcp.run()
238
+
239
+
240
+ if __name__ == "__main__":
241
+ main()
@@ -1,407 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Gemini MCP Server - Simple CLI Bridge
4
- Version 1.0.2
5
- A minimal MCP server to interface with Gemini AI via the gemini CLI.
6
- Created by @shelakh/elyin
7
- """
8
-
9
- import logging
10
- import os
11
- import shutil
12
- import subprocess
13
- import sys
14
- from typing import List, Optional
15
-
16
- from mcp.server.fastmcp import FastMCP
17
-
18
- # Configure logging for debugging timeout issues
19
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
20
- logger = logging.getLogger(__name__)
21
-
22
- # FastMCP instance with correct port configuration
23
- mcp = FastMCP("gemini-assistant", port=int(os.getenv("PORT", "8080")))
24
-
25
-
26
- def _normalize_model_name(model: Optional[str]) -> str:
27
- """
28
- Normalize user-provided model identifiers to canonical Gemini CLI model names.
29
- Defaults to gemini-2.5-flash when not provided or unrecognized.
30
-
31
- Accepted forms:
32
- - "flash", "2.5-flash", "gemini-2.5-flash"
33
- - "pro", "2.5-pro", "gemini-2.5-pro"
34
- """
35
- if not model:
36
- return "gemini-2.5-flash"
37
- value = model.strip().lower()
38
- # Common short aliases
39
- if value in {"flash", "2.5-flash", "gemini-2.5-flash"}:
40
- return "gemini-2.5-flash"
41
- if value in {"pro", "2.5-pro", "gemini-2.5-pro"}:
42
- return "gemini-2.5-pro"
43
- # If the caller passed a full model name, keep it
44
- if value.startswith("gemini-"):
45
- return value
46
- # Fallback to flash for anything else
47
- return "gemini-2.5-flash"
48
-
49
-
50
- def _get_timeout() -> int:
51
- """
52
- Get the timeout value from environment variable GEMINI_BRIDGE_TIMEOUT.
53
- Defaults to 60 seconds if not set or invalid.
54
-
55
- Returns:
56
- Timeout value in seconds (positive integer)
57
- """
58
- timeout_str = os.getenv("GEMINI_BRIDGE_TIMEOUT")
59
- logger.debug(
60
- "Reading timeout from environment: GEMINI_BRIDGE_TIMEOUT=%s", timeout_str
61
- )
62
-
63
- if not timeout_str:
64
- logger.info("GEMINI_BRIDGE_TIMEOUT not set, using default 60 seconds")
65
- return 60
66
-
67
- try:
68
- timeout = int(timeout_str)
69
- except ValueError:
70
- logger.warning(
71
- "Invalid GEMINI_BRIDGE_TIMEOUT value '%s' (must be integer). Using default 60 seconds.",
72
- timeout_str,
73
- )
74
- return 60
75
-
76
- if timeout <= 0:
77
- logger.warning(
78
- "Invalid GEMINI_BRIDGE_TIMEOUT value '%s' (must be positive). Using default 60 seconds.",
79
- timeout_str,
80
- )
81
- return 60
82
-
83
- logger.info("Using configured timeout: %s seconds", timeout)
84
- if timeout > 300:
85
- logger.warning(
86
- "Large timeout configured (%ss). This may cause long waits for failed operations.",
87
- timeout,
88
- )
89
- return timeout
90
-
91
- def execute_gemini_simple(query: str, directory: str = ".", model: Optional[str] = None) -> str:
92
- """
93
- Execute gemini CLI command for simple queries without file attachments.
94
-
95
- Args:
96
- query: The prompt to send to Gemini
97
- directory: Working directory for the command
98
- model: Optional model name (flash, pro, etc.)
99
-
100
- Returns:
101
- CLI output or error message
102
- """
103
- # Check if gemini CLI is available
104
- if not shutil.which("gemini"):
105
- return "Error: Gemini CLI not found. Install with: npm install -g @google/gemini-cli"
106
-
107
- # Validate directory
108
- if not os.path.isdir(directory):
109
- return f"Error: Directory does not exist: {directory}"
110
-
111
- # Build command - use stdin for input to avoid hanging
112
- selected_model = _normalize_model_name(model)
113
- cmd = ["gemini", "-m", selected_model]
114
-
115
- # Execute CLI command - simple timeout, no retries
116
- timeout = _get_timeout()
117
- logger.info(f"Executing Gemini CLI with timeout: {timeout}s, model: {selected_model}, directory: {directory}")
118
- logger.debug(f"Query length: {len(query)} characters")
119
-
120
- try:
121
- result = subprocess.run(
122
- cmd,
123
- cwd=directory,
124
- capture_output=True,
125
- text=True,
126
- timeout=timeout,
127
- input=query
128
- )
129
-
130
- logger.debug(f"Gemini CLI completed with return code: {result.returncode}")
131
-
132
- if result.returncode == 0:
133
- output = result.stdout.strip() if result.stdout.strip() else "No output from Gemini CLI"
134
- logger.info(f"Gemini CLI successful, output length: {len(output)} characters")
135
- return output
136
- else:
137
- error_msg = f"Gemini CLI Error: {result.stderr.strip()}"
138
- logger.error(error_msg)
139
- return error_msg
140
-
141
- except subprocess.TimeoutExpired:
142
- timeout_msg = f"Error: Gemini CLI command timed out after {timeout} seconds. Try increasing GEMINI_BRIDGE_TIMEOUT environment variable for large operations."
143
- logger.error(timeout_msg)
144
- return timeout_msg
145
- except Exception as e:
146
- error_msg = f"Error executing Gemini CLI: {str(e)}"
147
- logger.error(error_msg)
148
- return error_msg
149
-
150
-
151
- def execute_gemini_with_files(query: str, directory: str = ".", files: Optional[List[str]] = None, model: Optional[str] = None) -> str:
152
- """
153
- Execute gemini CLI command with file attachments.
154
-
155
- Args:
156
- query: The prompt to send to Gemini
157
- directory: Working directory for the command
158
- files: List of file paths to attach (relative to directory)
159
- model: Optional model name (flash, pro, etc.)
160
-
161
- Returns:
162
- CLI output or error message
163
- """
164
- # Check if gemini CLI is available
165
- if not shutil.which("gemini"):
166
- return "Error: Gemini CLI not found. Install with: npm install -g @google/gemini-cli"
167
-
168
- # Validate directory
169
- if not os.path.isdir(directory):
170
- return f"Error: Directory does not exist: {directory}"
171
-
172
- # Validate files parameter
173
- if not files:
174
- return "Error: No files provided for file attachment mode"
175
-
176
- # Build command - use stdin for input to avoid hanging
177
- selected_model = _normalize_model_name(model)
178
- cmd = ["gemini", "-m", selected_model]
179
-
180
- # Read and concatenate file contents
181
- file_contents = []
182
- for file_path in files:
183
- try:
184
- # Convert relative paths to absolute based on directory
185
- if not os.path.isabs(file_path):
186
- file_path = os.path.join(directory, file_path)
187
-
188
- if os.path.isfile(file_path):
189
- with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
190
- content = f.read()
191
- file_contents.append(f"=== {os.path.basename(file_path)} ===\n{content}")
192
- else:
193
- file_contents.append(f"=== {os.path.basename(file_path)} ===\n[File not found]")
194
- except Exception as e:
195
- file_contents.append(f"=== {os.path.basename(file_path)} ===\n[Error reading file: {str(e)}]")
196
-
197
- # Combine file contents with query
198
- stdin_content = "\n\n".join(file_contents) + "\n\n" + query
199
-
200
- # Execute CLI command - simple timeout, no retries
201
- timeout = _get_timeout()
202
- total_content_size = len(stdin_content)
203
- logger.info(f"Executing Gemini CLI with files, timeout: {timeout}s, model: {selected_model}, directory: {directory}")
204
- logger.info(f"File count: {len(files)}, total content size: {total_content_size} characters")
205
-
206
- # Warn about large content that might timeout
207
- if total_content_size > 100000: # 100KB threshold
208
- logger.warning(f"Large content size ({total_content_size} chars). Consider increasing timeout if you experience timeouts.")
209
-
210
- try:
211
- result = subprocess.run(
212
- cmd,
213
- cwd=directory,
214
- capture_output=True,
215
- text=True,
216
- timeout=timeout,
217
- input=stdin_content
218
- )
219
-
220
- logger.debug(f"Gemini CLI completed with return code: {result.returncode}")
221
-
222
- if result.returncode == 0:
223
- output = result.stdout.strip() if result.stdout.strip() else "No output from Gemini CLI"
224
- logger.info(f"Gemini CLI successful, output length: {len(output)} characters")
225
- return output
226
- else:
227
- error_msg = f"Gemini CLI Error: {result.stderr.strip()}"
228
- logger.error(error_msg)
229
- return error_msg
230
-
231
- except subprocess.TimeoutExpired:
232
- timeout_msg = f"Error: Gemini CLI command timed out after {timeout} seconds with {len(files)} files ({total_content_size} chars). Try increasing GEMINI_BRIDGE_TIMEOUT environment variable (current: {os.getenv('GEMINI_BRIDGE_TIMEOUT', 'not set')})."
233
- logger.error(timeout_msg)
234
- return timeout_msg
235
- except Exception as e:
236
- error_msg = f"Error executing Gemini CLI: {str(e)}"
237
- logger.error(error_msg)
238
- return error_msg
239
-
240
-
241
- @mcp.tool()
242
- def consult_gemini(
243
- query: str,
244
- directory: str,
245
- model: Optional[str] = None
246
- ) -> str:
247
- """
248
- Send a query directly to Gemini CLI.
249
-
250
- This is the core function - a direct bridge between Claude and Gemini.
251
- No caching, no sessions, no complexity. Just execute and return.
252
-
253
- Args:
254
- query: The question or prompt to send to Gemini
255
- directory: Working directory (required)
256
- model: Optional model name (flash, pro, etc.)
257
-
258
- Returns:
259
- Gemini's response
260
- """
261
- return execute_gemini_simple(query, directory, model)
262
-
263
-
264
- @mcp.tool()
265
- def consult_gemini_with_files(
266
- query: str,
267
- directory: str,
268
- files: Optional[List[str]] = None,
269
- model: Optional[str] = None
270
- ) -> str:
271
- """
272
- Send a query to Gemini CLI with file attachments.
273
-
274
- Files are read and concatenated into the prompt. Simple and direct.
275
-
276
- Args:
277
- query: The question or prompt to send to Gemini
278
- directory: Working directory (required)
279
- files: List of file paths to attach (relative to directory)
280
- model: Optional model name (flash, pro, etc.)
281
-
282
- Returns:
283
- Gemini's response with file context
284
- """
285
- if not files:
286
- return "Error: files parameter is required for consult_gemini_with_files"
287
- return execute_gemini_with_files(query, directory, files, model)
288
-
289
-
290
- @mcp.tool()
291
- def get_debug_info() -> str:
292
- """
293
- Get diagnostic information about the Gemini Bridge configuration.
294
-
295
- Useful for troubleshooting timeout issues and verifying setup.
296
-
297
- Returns:
298
- Formatted debug information including timeout configuration,
299
- environment variables, and system status
300
- """
301
- debug_info = []
302
- debug_info.append("=== Gemini Bridge Debug Information ===\n")
303
-
304
- # Timeout configuration
305
- timeout_env = os.getenv("GEMINI_BRIDGE_TIMEOUT")
306
- actual_timeout = _get_timeout()
307
- debug_info.append("Timeout Configuration:")
308
- debug_info.append(f" GEMINI_BRIDGE_TIMEOUT env var: {timeout_env or 'not set'}")
309
- debug_info.append(f" Actual timeout used: {actual_timeout} seconds")
310
-
311
- if actual_timeout == 60 and not timeout_env:
312
- debug_info.append(" ⚠️ Using default timeout. Set GEMINI_BRIDGE_TIMEOUT=240 for large operations.")
313
- elif actual_timeout < 120:
314
- debug_info.append(" ⚠️ Timeout may be too low for large files or complex queries.")
315
- elif actual_timeout > 300:
316
- debug_info.append(f" ⚠️ Very high timeout configured. Failed operations will wait {actual_timeout}s.")
317
- else:
318
- debug_info.append(" ✓ Timeout looks reasonable for most operations.")
319
-
320
- debug_info.append("")
321
-
322
- # Gemini CLI status
323
- gemini_path = shutil.which("gemini")
324
- debug_info.append("Gemini CLI Status:")
325
- debug_info.append(f" CLI available: {'✓ Yes' if gemini_path else '✗ No'}")
326
- if gemini_path:
327
- debug_info.append(f" CLI path: {gemini_path}")
328
- try:
329
- # Try to get version
330
- result = subprocess.run(
331
- ["gemini", "--version"],
332
- capture_output=True,
333
- text=True,
334
- timeout=5
335
- )
336
- if result.returncode == 0:
337
- version = result.stdout.strip()
338
- debug_info.append(f" CLI version: {version}")
339
- else:
340
- debug_info.append(f" CLI version check failed: {result.stderr.strip()}")
341
- except Exception as e:
342
- debug_info.append(f" CLI version check error: {str(e)}")
343
- else:
344
- debug_info.append(" ✗ Install with: npm install -g @google/gemini-cli")
345
-
346
- debug_info.append("")
347
- # Environment details
348
- debug_info.append("Environment:")
349
- debug_info.append(f" Python version: {sys.version.split()[0]}")
350
- debug_info.append(f" Current working directory: {os.getcwd()}")
351
- debug_info.append(f" PORT: {os.getenv('PORT', '8080')}")
352
-
353
- # Check authentication status
354
- try:
355
- result = subprocess.run(
356
- ["gemini", "auth", "status"],
357
- capture_output=True,
358
- text=True,
359
- timeout=10,
360
- cwd="."
361
- )
362
- if result.returncode == 0:
363
- debug_info.append(" Authentication: ✓ Logged in")
364
- else:
365
- debug_info.append(" Authentication: ✗ Not logged in - run 'gemini auth login'")
366
- except Exception as e:
367
- debug_info.append(f" Authentication status check failed: {str(e)}")
368
-
369
- debug_info.append("")
370
-
371
- # Recent environment variables that might affect operation
372
- relevant_env_vars = [
373
- "GEMINI_BRIDGE_TIMEOUT", "NODE_PATH", "PATH", "HOME",
374
- "GOOGLE_APPLICATION_CREDENTIALS", "GOOGLE_CLOUD_PROJECT"
375
- ]
376
-
377
- debug_info.append("Relevant Environment Variables:")
378
- for var in relevant_env_vars:
379
- value = os.getenv(var)
380
- if value:
381
- # Truncate very long values
382
- display_value = value[:100] + "..." if len(value) > 100 else value
383
- debug_info.append(f" {var}: {display_value}")
384
- else:
385
- debug_info.append(f" {var}: not set")
386
-
387
- debug_info.append("")
388
- debug_info.append("=== End Debug Information ===")
389
-
390
- return "\n".join(debug_info)
391
-
392
-
393
- def main():
394
- """Entry point for the MCP server."""
395
- port = int(os.getenv("PORT", "8080"))
396
- timeout = _get_timeout() # Log timeout configuration at startup
397
-
398
- logger.info(f"Starting Gemini Bridge MCP Server on port {port}")
399
- logger.info(f"Configured timeout: {timeout} seconds")
400
- logger.info(f"Gemini CLI available: {shutil.which('gemini') is not None}")
401
-
402
- # Run the MCP server with SSE transport (port is managed by the library)
403
- mcp.run(transport="sse")
404
-
405
-
406
- if __name__ == "__main__":
407
- main()
File without changes
File without changes