docs: Improve Quick Start instructions for better clarity
- Add explicit step to clone the repository first - Include instructions for accessing config via Claude Desktop UI - Clarify that users must replace placeholder paths with actual paths - Add note to remember the cloned directory path 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
32
README.md
32
README.md
@@ -13,11 +13,27 @@ This server acts as a developer assistant that augments Claude Code when you nee
|
||||
|
||||
## 🚀 Quick Start for Claude Code
|
||||
|
||||
### 1. Configure in Claude Desktop
|
||||
### 1. Clone the Repository
|
||||
|
||||
Add to your Claude Desktop configuration file:
|
||||
First, clone this repository to your local machine:
|
||||
```bash
|
||||
git clone https://github.com/BeehiveInnovations/gemini-mcp-server.git
|
||||
cd gemini-mcp-server
|
||||
```
|
||||
|
||||
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
||||
Note the full path to this directory - you'll need it for the configuration.
|
||||
|
||||
### 2. Configure in Claude Desktop
|
||||
|
||||
You can access the configuration file in two ways:
|
||||
- **Through Claude Desktop**: Open Claude Desktop → Settings → Developer → Edit Config
|
||||
- **Direct file access**:
|
||||
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
||||
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
||||
|
||||
Add the following configuration, replacing the path with your actual directory path:
|
||||
|
||||
**macOS**:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
@@ -31,7 +47,7 @@ Add to your Claude Desktop configuration file:
|
||||
}
|
||||
```
|
||||
|
||||
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
||||
**Windows**:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
@@ -45,18 +61,20 @@ Add to your Claude Desktop configuration file:
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Restart Claude Desktop
|
||||
**Important**: Replace `/path/to/gemini-mcp-server` with the actual path where you cloned the repository.
|
||||
|
||||
### 3. Restart Claude Desktop
|
||||
|
||||
After adding the configuration, restart Claude Desktop. You'll see "gemini" in the MCP servers list.
|
||||
|
||||
### 3. Add to Claude Code
|
||||
### 4. Add to Claude Code
|
||||
|
||||
To make the server available in Claude Code, run:
|
||||
```bash
|
||||
claude mcp add-from-claude-desktop -s user
|
||||
```
|
||||
|
||||
### 4. Start Using Natural Language
|
||||
### 5. Start Using Natural Language
|
||||
|
||||
Just talk to Claude naturally:
|
||||
- "Use Gemini to analyze this large file..."
|
||||
|
||||
28
tests/conftest.py
Normal file
28
tests/conftest.py
Normal file
@@ -0,0 +1,28 @@
|
||||
"""
|
||||
Pytest configuration for Gemini MCP Server tests
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Ensure the parent directory is in the Python path for imports
|
||||
parent_dir = Path(__file__).resolve().parent.parent
|
||||
if str(parent_dir) not in sys.path:
|
||||
sys.path.insert(0, str(parent_dir))
|
||||
|
||||
# Set dummy API key for tests if not already set
|
||||
if "GEMINI_API_KEY" not in os.environ:
|
||||
os.environ["GEMINI_API_KEY"] = "dummy-key-for-tests"
|
||||
|
||||
# Configure asyncio for Windows compatibility
|
||||
if sys.platform == "win32":
|
||||
import asyncio
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
# Pytest configuration
|
||||
def pytest_configure(config):
|
||||
"""Configure pytest with custom markers"""
|
||||
config.addinivalue_line(
|
||||
"markers", "asyncio: mark test as async"
|
||||
)
|
||||
@@ -7,11 +7,15 @@ import json
|
||||
from unittest.mock import Mock, patch, AsyncMock
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
# Add parent directory to path for imports in a cross-platform way
|
||||
parent_dir = Path(__file__).resolve().parent.parent
|
||||
if str(parent_dir) not in sys.path:
|
||||
sys.path.insert(0, str(parent_dir))
|
||||
|
||||
from gemini_server import (
|
||||
try:
|
||||
from gemini_server import (
|
||||
GeminiChatRequest,
|
||||
CodeAnalysisRequest,
|
||||
read_file_content,
|
||||
@@ -21,6 +25,27 @@ from gemini_server import (
|
||||
DEVELOPER_SYSTEM_PROMPT,
|
||||
DEFAULT_MODEL
|
||||
)
|
||||
except ImportError as e:
|
||||
# If import fails, try alternative import method
|
||||
import importlib.util
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"gemini_server",
|
||||
parent_dir / "gemini_server.py"
|
||||
)
|
||||
gemini_server = importlib.util.module_from_spec(spec)
|
||||
sys.modules["gemini_server"] = gemini_server
|
||||
spec.loader.exec_module(gemini_server)
|
||||
|
||||
from gemini_server import (
|
||||
GeminiChatRequest,
|
||||
CodeAnalysisRequest,
|
||||
read_file_content,
|
||||
prepare_code_context,
|
||||
handle_list_tools,
|
||||
handle_call_tool,
|
||||
DEVELOPER_SYSTEM_PROMPT,
|
||||
DEFAULT_MODEL
|
||||
)
|
||||
|
||||
|
||||
class TestModels:
|
||||
@@ -66,7 +91,7 @@ class TestFileOperations:
|
||||
def test_read_file_content_success(self, tmp_path):
|
||||
"""Test successful file reading"""
|
||||
test_file = tmp_path / "test.py"
|
||||
test_file.write_text("def hello():\n return 'world'")
|
||||
test_file.write_text("def hello():\n return 'world'", encoding='utf-8')
|
||||
|
||||
content = read_file_content(str(test_file))
|
||||
assert "=== File:" in content
|
||||
@@ -75,7 +100,11 @@ class TestFileOperations:
|
||||
|
||||
def test_read_file_content_not_found(self):
|
||||
"""Test reading non-existent file"""
|
||||
content = read_file_content("/nonexistent/file.py")
|
||||
# Use a path that's guaranteed not to exist on any platform
|
||||
nonexistent_path = os.path.join(
|
||||
os.path.sep, "nonexistent_dir_12345", "nonexistent_file.py"
|
||||
)
|
||||
content = read_file_content(nonexistent_path)
|
||||
assert "Error: File not found" in content
|
||||
|
||||
def test_read_file_content_directory(self, tmp_path):
|
||||
@@ -86,9 +115,9 @@ class TestFileOperations:
|
||||
def test_prepare_code_context_with_files(self, tmp_path):
|
||||
"""Test preparing context from files"""
|
||||
file1 = tmp_path / "file1.py"
|
||||
file1.write_text("print('file1')")
|
||||
file1.write_text("print('file1')", encoding='utf-8')
|
||||
file2 = tmp_path / "file2.py"
|
||||
file2.write_text("print('file2')")
|
||||
file2.write_text("print('file2')", encoding='utf-8')
|
||||
|
||||
context = prepare_code_context([str(file1), str(file2)], None)
|
||||
assert "file1.py" in context
|
||||
@@ -106,7 +135,7 @@ class TestFileOperations:
|
||||
def test_prepare_code_context_mixed(self, tmp_path):
|
||||
"""Test preparing context from both files and code"""
|
||||
test_file = tmp_path / "test.py"
|
||||
test_file.write_text("# From file")
|
||||
test_file.write_text("# From file", encoding='utf-8')
|
||||
code = "# Direct code"
|
||||
|
||||
context = prepare_code_context([str(test_file)], code)
|
||||
@@ -136,7 +165,7 @@ class TestToolHandlers:
|
||||
assert "Unknown tool" in result[0].text
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('gemini_server.genai.GenerativeModel')
|
||||
@patch('google.generativeai.GenerativeModel')
|
||||
async def test_handle_call_tool_chat_success(self, mock_model):
|
||||
"""Test successful chat tool call"""
|
||||
# Mock the response
|
||||
@@ -163,7 +192,7 @@ class TestToolHandlers:
|
||||
assert call_args['generation_config']['temperature'] == 0.5
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('gemini_server.genai.GenerativeModel')
|
||||
@patch('google.generativeai.GenerativeModel')
|
||||
async def test_handle_call_tool_chat_with_developer_prompt(self, mock_model):
|
||||
"""Test chat tool uses developer prompt when no system prompt provided"""
|
||||
mock_response = Mock()
|
||||
@@ -190,12 +219,12 @@ class TestToolHandlers:
|
||||
assert "Must provide either 'files' or 'code'" in result[0].text
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('gemini_server.genai.GenerativeModel')
|
||||
@patch('google.generativeai.GenerativeModel')
|
||||
async def test_handle_call_tool_analyze_code_success(self, mock_model, tmp_path):
|
||||
"""Test successful code analysis"""
|
||||
# Create test file
|
||||
test_file = tmp_path / "test.py"
|
||||
test_file.write_text("def hello(): pass")
|
||||
test_file.write_text("def hello(): pass", encoding='utf-8')
|
||||
|
||||
# Mock response
|
||||
mock_response = Mock()
|
||||
@@ -215,7 +244,7 @@ class TestToolHandlers:
|
||||
assert result[0].text == "Analysis result"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('gemini_server.genai.list_models')
|
||||
@patch('google.generativeai.list_models')
|
||||
async def test_handle_call_tool_list_models(self, mock_list_models):
|
||||
"""Test listing models"""
|
||||
# Mock model data
|
||||
@@ -240,7 +269,7 @@ class TestErrorHandling:
|
||||
"""Test error handling scenarios"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('gemini_server.genai.GenerativeModel')
|
||||
@patch('google.generativeai.GenerativeModel')
|
||||
async def test_handle_call_tool_chat_api_error(self, mock_model):
|
||||
"""Test handling API errors in chat"""
|
||||
mock_instance = Mock()
|
||||
@@ -253,7 +282,7 @@ class TestErrorHandling:
|
||||
assert "API Error" in result[0].text
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('gemini_server.genai.GenerativeModel')
|
||||
@patch('google.generativeai.GenerativeModel')
|
||||
async def test_handle_call_tool_chat_blocked_response(self, mock_model):
|
||||
"""Test handling blocked responses"""
|
||||
mock_response = Mock()
|
||||
|
||||
Reference in New Issue
Block a user