feat: add analyze_file and extended_think tools for better collaboration

New tools:
- analyze_file: Clean file analysis without terminal clutter
  - Always uses file paths, never shows content in terminal
  - Server reads files directly and sends to Gemini
  - Replaces analyze_code for file analysis use cases

- extended_think: Deep collaborative thinking with Claude
  - Takes Claude's analysis/thoughts as input for deeper exploration
  - Supports optional file context and focus areas
  - Higher temperature (0.7) for creative problem-solving
  - Designed for validating and extending Claude's analysis

Improvements:
- Added specialized system prompt for extended thinking
- Updated documentation with examples and workflows
- Added comprehensive tests for new tools
- Kept analyze_code for backward compatibility

This enables Claude and Gemini to work as true development partners,
with Claude doing primary analysis and Gemini providing validation,
alternative perspectives, and extended context processing.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Fahad
2025-06-08 22:01:46 +04:00
parent f0ff610a7f
commit 0db3c06381
3 changed files with 452 additions and 36 deletions

View File

@@ -110,12 +110,15 @@ claude mcp add-from-claude-desktop -s user
### 5. Start Using Natural Language
Just talk to Claude naturally:
- "Use Gemini to analyze this large file..."
- "Ask Gemini to review the architecture of these files..."
- "Have Gemini check this codebase for security issues..."
- "Use gemini analyze_file on main.py to find bugs"
- "Share your analysis with gemini extended_think for deeper insights"
- "Ask gemini to review the architecture using analyze_file"
**Pro tip:** For clean terminal output when analyzing files, mention "files parameter" in your prompt:
- "Use gemini analyze_code with files=['config.py'] to review the configuration"
**Key tools:**
- `analyze_file` - Clean file analysis without terminal clutter
- `extended_think` - Collaborative deep thinking with Claude's analysis
- `chat` - General conversations
- `analyze_code` - Legacy tool (prefer analyze_file for files)
## How It Works
@@ -161,30 +164,48 @@ General-purpose developer conversations with Gemini.
"Use Gemini to explain the tradeoffs between different authentication strategies"
```
### `analyze_code`
Specialized tool for analyzing large files or multiple files that exceed Claude's limits.
### `analyze_code` (Legacy)
Analyzes code files or snippets. For better terminal output, use `analyze_file` instead.
### `analyze_file` (Recommended for Files)
Clean file analysis - always uses file paths, never shows content in terminal.
**Example uses:**
```
"Use Gemini to analyze /src/core/engine.py and identify performance bottlenecks"
"Have Gemini review these files together: auth.py, users.py, permissions.py"
"Use gemini analyze_file on README.md to find issues"
"Ask gemini to analyze_file main.py for performance problems"
"Have gemini analyze_file on auth.py, users.py, and permissions.py together"
```
**Important - Avoiding Terminal Clutter:**
When analyzing files, be explicit about using the files parameter to prevent Claude from showing the entire file content in the terminal:
**Benefits:**
- Terminal always stays clean - only shows "Analyzing N file(s)"
- Server reads files directly and sends to Gemini
- No need to worry about prompt phrasing
- Supports multiple files in one request
**Good prompts** (clean terminal output):
- "Use gemini analyze_code with files=['README.md'] to check for issues"
- "Ask gemini to analyze main.py using the files parameter"
- "Use gemini to analyze README.md - use the files parameter with the path"
- "Call gemini analyze_code passing config.json in the files parameter"
### `extended_think`
Collaborate with Gemini on complex problems by sharing Claude's analysis for deeper insights.
**Avoid these** (will show entire file in terminal):
- "Get gemini's feedback on this README file"
- "Can you analyze this file with gemini?"
- "Ask gemini about the code in main.py"
**Example uses:**
```
"Share your analysis with gemini extended_think for deeper insights"
"Use gemini extended_think to validate and extend your architectural design"
"Ask gemini to extend your thinking on this security analysis"
```
The server reads files directly when you use the files parameter, keeping your terminal clean while still sending the full content to Gemini.
**Advanced usage with focus areas:**
```
"Use gemini extended_think with focus='performance' to drill into scaling issues"
"Share your design with gemini extended_think focusing on security vulnerabilities"
"Get gemini to extend your analysis with focus on edge cases"
```
**Features:**
- Takes Claude's thoughts, plans, or analysis as input
- Optional file context for reference
- Configurable focus areas (architecture, bugs, performance, security)
- Higher temperature (0.7) for creative problem-solving
- Designed for collaborative thinking, not just code review
### `list_models`
Lists available Gemini models (defaults to 2.5 Pro Preview).
@@ -269,28 +290,40 @@ This prevents Claude from displaying the entire file content in your terminal.
### Common Workflows
#### 1. **Claude's Extended Thinking + Gemini Validation**
#### 1. **Extended Thinking Partnership**
```
You: "Design a distributed task queue system"
Claude: [provides detailed architecture and implementation plan]
You: "Share your complete design with Gemini and ask it to identify potential race conditions or failure modes"
Gemini: [analyzes and finds edge cases]
You: "Use gemini extended_think to validate and extend this design"
Gemini: [identifies gaps, suggests alternatives, finds edge cases]
You: "Address the issues Gemini found"
Claude: [updates design with safeguards]
Claude: [updates design with improvements]
```
#### 2. **Large File Analysis**
#### 2. **Clean File Analysis (No Terminal Clutter)**
```
"Use Gemini to analyze /path/to/large/file.py and summarize its architecture"
"Have Gemini trace all function calls in this module"
"Ask Gemini to identify unused code in this file"
"Use gemini analyze_file on engine.py to find performance issues"
"Ask gemini to analyze_file database.py and suggest optimizations"
"Have gemini analyze_file on all files in /src/core/"
```
#### 3. **Multi-File Context**
#### 3. **Multi-File Architecture Review**
```
"Use Gemini to analyze how auth.py, users.py, and permissions.py work together"
"Have Gemini map the data flow between these components"
"Ask Gemini to find all circular dependencies in /src"
"Use gemini analyze_file on auth.py, users.py, permissions.py to map dependencies"
"Ask gemini to analyze_file the entire /src/api/ directory for security issues"
"Have gemini analyze_file all model files to check for N+1 queries"
```
#### 4. **Deep Collaborative Analysis**
```
Claude: "Here's my analysis of the memory leak: [detailed investigation]"
You: "Share this with gemini extended_think focusing on root causes"
Claude: "I've designed this caching strategy: [detailed design]"
You: "Use gemini extended_think with focus='performance' to stress-test this design"
Claude: "Here's my security assessment: [findings]"
You: "Get gemini to extended_think on this with files=['auth.py', 'crypto.py'] for context"
```
#### 4. **Claude-Driven Design with Gemini Validation**

View File

@@ -54,6 +54,29 @@ Your approach:
Remember: You're augmenting Claude Code's capabilities, especially for tasks requiring \
extensive context or deep analysis that might exceed Claude's token limits."""
# Extended thinking system prompt for collaborative analysis
EXTENDED_THINKING_PROMPT = """You are a senior development partner collaborating with Claude Code on complex problems. \
Claude has shared their analysis with you for deeper exploration and validation.
Your role is to:
1. Build upon Claude's thinking - identify gaps, extend ideas, and suggest alternatives
2. Challenge assumptions constructively and identify potential issues
3. Provide concrete, actionable insights that complement Claude's analysis
4. Focus on aspects Claude might have missed or couldn't fully explore
5. Suggest implementation strategies and architectural improvements
Key areas to consider:
- Edge cases and failure modes Claude might have overlooked
- Performance implications at scale
- Security vulnerabilities or attack vectors
- Maintainability and technical debt considerations
- Alternative approaches or design patterns
- Integration challenges with existing systems
- Testing strategies for complex scenarios
Be direct and technical. Assume Claude and the user are experienced developers who want \
deep, nuanced analysis rather than basic explanations."""
class GeminiChatRequest(BaseModel):
"""Request model for Gemini chat"""
@@ -102,6 +125,59 @@ class CodeAnalysisRequest(BaseModel):
)
class FileAnalysisRequest(BaseModel):
"""Request model for file analysis"""
files: List[str] = Field(..., description="List of file paths to analyze")
question: str = Field(
..., description="Question or analysis request about the files"
)
system_prompt: Optional[str] = Field(
None, description="Optional system prompt for context"
)
max_tokens: Optional[int] = Field(
8192, description="Maximum number of tokens in response"
)
temperature: Optional[float] = Field(
0.2,
description="Temperature for analysis (0-1, default 0.2 for high accuracy)",
)
model: Optional[str] = Field(
DEFAULT_MODEL, description=f"Model to use (defaults to {DEFAULT_MODEL})"
)
class ExtendedThinkRequest(BaseModel):
"""Request model for extended thinking with Gemini"""
thought_process: str = Field(
..., description="Claude's analysis, thoughts, plans, or outlines to extend"
)
context: Optional[str] = Field(
None, description="Additional context about the problem or goal"
)
files: Optional[List[str]] = Field(
None, description="Optional file paths for additional context"
)
focus: Optional[str] = Field(
None,
description="Specific focus area: architecture, bugs, performance, security, etc.",
)
system_prompt: Optional[str] = Field(
None, description="Optional system prompt for context"
)
max_tokens: Optional[int] = Field(
8192, description="Maximum number of tokens in response"
)
temperature: Optional[float] = Field(
0.7,
description="Temperature for creative thinking (0-1, default 0.7 for balanced creativity)",
)
model: Optional[str] = Field(
DEFAULT_MODEL, description=f"Model to use (defaults to {DEFAULT_MODEL})"
)
# Create the MCP server instance
server: Server = Server("gemini-server")
@@ -237,7 +313,8 @@ async def handle_list_tools() -> List[Tool]:
),
Tool(
name="analyze_code",
description="Analyze code files or snippets with Gemini's 1M context window. For large content, use file paths to avoid terminal clutter.",
description="Analyze code files or snippets with Gemini's 1M context window. "
"For large content, use file paths to avoid terminal clutter.",
inputSchema={
"type": "object",
"properties": {
@@ -248,7 +325,8 @@ async def handle_list_tools() -> List[Tool]:
},
"code": {
"type": "string",
"description": "Direct code content to analyze (use for small snippets only; prefer files for large content)",
"description": "Direct code content to analyze "
"(use for small snippets only; prefer files for large content)",
},
"question": {
"type": "string",
@@ -289,6 +367,94 @@ async def handle_list_tools() -> List[Tool]:
description="Get the version and metadata of the Gemini MCP Server",
inputSchema={"type": "object", "properties": {}},
),
Tool(
name="analyze_file",
description="Analyze files with Gemini - always uses file paths for clean terminal output",
inputSchema={
"type": "object",
"properties": {
"files": {
"type": "array",
"items": {"type": "string"},
"description": "List of file paths to analyze",
},
"question": {
"type": "string",
"description": "Question or analysis request about the files",
},
"system_prompt": {
"type": "string",
"description": "Optional system prompt for context",
},
"max_tokens": {
"type": "integer",
"description": "Maximum number of tokens in response",
"default": 8192,
},
"temperature": {
"type": "number",
"description": "Temperature for analysis (0-1, default 0.2 for high accuracy)",
"default": 0.2,
"minimum": 0,
"maximum": 1,
},
"model": {
"type": "string",
"description": f"Model to use (defaults to {DEFAULT_MODEL})",
"default": DEFAULT_MODEL,
},
},
"required": ["files", "question"],
},
),
Tool(
name="extended_think",
description="Collaborate with Gemini on complex problems - share Claude's analysis for deeper insights",
inputSchema={
"type": "object",
"properties": {
"thought_process": {
"type": "string",
"description": "Claude's analysis, thoughts, plans, or outlines to extend",
},
"context": {
"type": "string",
"description": "Additional context about the problem or goal",
},
"files": {
"type": "array",
"items": {"type": "string"},
"description": "Optional file paths for additional context",
},
"focus": {
"type": "string",
"description": "Specific focus area: architecture, bugs, performance, security, etc.",
},
"system_prompt": {
"type": "string",
"description": "Optional system prompt for context",
},
"max_tokens": {
"type": "integer",
"description": "Maximum number of tokens in response",
"default": 8192,
},
"temperature": {
"type": "number",
"description": "Temperature for creative thinking (0-1, default 0.7)",
"default": 0.7,
"minimum": 0,
"maximum": 1,
},
"model": {
"type": "string",
"description": f"Model to use (defaults to {DEFAULT_MODEL})",
"default": DEFAULT_MODEL,
},
},
"required": ["thought_process"],
},
),
]
@@ -509,6 +675,165 @@ For updates, visit: https://github.com/BeehiveInnovations/gemini-mcp-server""",
)
]
elif name == "analyze_file":
# Validate request
request_file = FileAnalysisRequest(**arguments)
try:
# Prepare code context from files
code_context, summary = prepare_code_context(request_file.files, None)
# Count approximate tokens
estimated_tokens = len(code_context) // 4
if estimated_tokens > MAX_CONTEXT_TOKENS:
return [
TextContent(
type="text",
text=f"Error: File content too large (~{estimated_tokens:,} tokens). "
f"Maximum is {MAX_CONTEXT_TOKENS:,} tokens.",
)
]
# Use the specified model with optimized settings
model_name = request_file.model or DEFAULT_MODEL
temperature = (
request_file.temperature if request_file.temperature is not None else 0.2
)
max_tokens = request_file.max_tokens if request_file.max_tokens is not None else 8192
model = genai.GenerativeModel(
model_name=model_name,
generation_config={
"temperature": temperature,
"max_output_tokens": max_tokens,
"candidate_count": 1,
},
)
# Prepare prompt
system_prompt = request_file.system_prompt or DEVELOPER_SYSTEM_PROMPT
full_prompt = f"""{system_prompt}
=== USER REQUEST ===
{request_file.question}
=== END USER REQUEST ===
=== FILES TO ANALYZE ===
{code_context}
=== END FILES ===
Please analyze the files above and respond to the user's request."""
# Generate response
response = model.generate_content(full_prompt)
# Handle response
if response.candidates and response.candidates[0].content.parts:
text = response.candidates[0].content.parts[0].text
else:
finish_reason = (
response.candidates[0].finish_reason
if response.candidates
else "Unknown"
)
text = f"Response blocked or incomplete. Finish reason: {finish_reason}"
# Create a brief summary for terminal
brief_summary = f"Analyzing {len(request_file.files)} file(s)"
response_text = f"{brief_summary}\n\nGemini's Analysis:\n{text}"
return [TextContent(type="text", text=response_text)]
except Exception as e:
return [TextContent(type="text", text=f"Error analyzing files: {str(e)}")]
elif name == "extended_think":
# Validate request
request_think = ExtendedThinkRequest(**arguments)
try:
# Prepare context parts
context_parts = [
f"=== CLAUDE'S ANALYSIS ===\n{request_think.thought_process}\n=== END CLAUDE'S ANALYSIS ==="
]
if request_think.context:
context_parts.append(
f"\n=== ADDITIONAL CONTEXT ===\n{request_think.context}\n=== END CONTEXT ==="
)
# Add file contents if provided
if request_think.files:
file_context, _ = prepare_code_context(request_think.files, None)
context_parts.append(
f"\n=== REFERENCE FILES ===\n{file_context}\n=== END FILES ==="
)
full_context = "\n".join(context_parts)
# Check token limits
estimated_tokens = len(full_context) // 4
if estimated_tokens > MAX_CONTEXT_TOKENS:
return [
TextContent(
type="text",
text=f"Error: Context too large (~{estimated_tokens:,} tokens). "
f"Maximum is {MAX_CONTEXT_TOKENS:,} tokens.",
)
]
# Use the specified model with creative settings
model_name = request_think.model or DEFAULT_MODEL
temperature = (
request_think.temperature if request_think.temperature is not None else 0.7
)
max_tokens = request_think.max_tokens if request_think.max_tokens is not None else 8192
model = genai.GenerativeModel(
model_name=model_name,
generation_config={
"temperature": temperature,
"max_output_tokens": max_tokens,
"candidate_count": 1,
},
)
# Prepare prompt with focus area if specified
system_prompt = request_think.system_prompt or EXTENDED_THINKING_PROMPT
focus_instruction = ""
if request_think.focus:
focus_instruction = f"\n\nFOCUS AREA: Please pay special attention to {request_think.focus} aspects."
full_prompt = f"""{system_prompt}{focus_instruction}
{full_context}
Build upon Claude's analysis with deeper insights, alternative approaches, and critical evaluation."""
# Generate response
response = model.generate_content(full_prompt)
# Handle response
if response.candidates and response.candidates[0].content.parts:
text = response.candidates[0].content.parts[0].text
else:
finish_reason = (
response.candidates[0].finish_reason
if response.candidates
else "Unknown"
)
text = f"Response blocked or incomplete. Finish reason: {finish_reason}"
# Create response with clear attribution
response_text = f"Extended Analysis by Gemini:\n\n{text}"
return [TextContent(type="text", text=response_text)]
except Exception as e:
return [
TextContent(type="text", text=f"Error in extended thinking: {str(e)}")
]
else:
return [TextContent(type="text", text=f"Unknown tool: {name}")]

View File

@@ -139,13 +139,15 @@ class TestToolHandlers:
async def test_handle_list_tools(self):
"""Test listing available tools"""
tools = await handle_list_tools()
assert len(tools) == 4
assert len(tools) == 6
tool_names = [tool.name for tool in tools]
assert "chat" in tool_names
assert "analyze_code" in tool_names
assert "list_models" in tool_names
assert "get_version" in tool_names
assert "analyze_file" in tool_names
assert "extended_think" in tool_names
@pytest.mark.asyncio
async def test_handle_call_tool_unknown(self):
@@ -254,6 +256,62 @@ class TestToolHandlers:
assert models[0]["name"] == "test-model"
assert models[0]["is_default"] == False
@pytest.mark.asyncio
@patch("google.generativeai.GenerativeModel")
async def test_handle_call_tool_analyze_file_success(self, mock_model, tmp_path):
"""Test successful file analysis with analyze_file tool"""
# Create test file
test_file = tmp_path / "test.py"
test_file.write_text("def hello(): pass", encoding="utf-8")
# Mock response
mock_response = Mock()
mock_response.candidates = [Mock()]
mock_response.candidates[0].content.parts = [Mock(text="File analysis result")]
mock_instance = Mock()
mock_instance.generate_content.return_value = mock_response
mock_model.return_value = mock_instance
result = await handle_call_tool(
"analyze_file", {"files": [str(test_file)], "question": "Analyze this file"}
)
assert len(result) == 1
response_text = result[0].text
assert "Analyzing 1 file(s)" in response_text
assert "Gemini's Analysis:" in response_text
assert "File analysis result" in response_text
@pytest.mark.asyncio
@patch("google.generativeai.GenerativeModel")
async def test_handle_call_tool_extended_think_success(self, mock_model):
"""Test successful extended thinking"""
# Mock response
mock_response = Mock()
mock_response.candidates = [Mock()]
mock_response.candidates[0].content.parts = [
Mock(text="Extended thinking result")
]
mock_instance = Mock()
mock_instance.generate_content.return_value = mock_response
mock_model.return_value = mock_instance
result = await handle_call_tool(
"extended_think",
{
"thought_process": "Claude's analysis of the problem...",
"context": "Building a distributed system",
"focus": "performance",
},
)
assert len(result) == 1
response_text = result[0].text
assert "Extended Analysis by Gemini:" in response_text
assert "Extended thinking result" in response_text
class TestErrorHandling:
"""Test error handling scenarios"""