This commit is contained in:
2025-05-30 20:07:06 +09:00
parent 9866da625d
commit a7b61fe07d
3 changed files with 882 additions and 0 deletions

View File

@ -0,0 +1,246 @@
# Claude CodeでClaude Code的環境を作る方法
Claude Code**で**Claude Code**のような**ことを実現する様々なアプローチをご紹介!
## 🎯 方法1: MCP Server経由でローカルLLMに委譲
### claude-code-mcp を使用
```bash
# Claude Code MCPサーバーのセットアップ
git clone https://github.com/steipete/claude-code-mcp
cd claude-code-mcp
# Claude Codeをローカルで呼び出すMCPサーバーとして動作
npm install
npm start
```
**仕組み:**
- Claude Code → MCP Server → ローカルLLM → 結果を返す
- Claude Codeを全権限バイパス--dangerously-skip-permissionsで実行
- Agent in Agent 構造の実現
## 🎯 方法2: Claude Desktop + Custom MCP Server
### カスタムMCPサーバーでローカルLLM統合
```python
# custom_llm_mcp_server.py
import asyncio
import json
from mcp.server import Server
from mcp.types import Tool, TextContent
import requests
app = Server("local-llm-mcp")
@app.tool("run_local_llm")
async def run_local_llm(prompt: str, model: str = "qwen2.5-coder:14b") -> str:
"""ローカルLLMでコード生成・分析を実行"""
response = requests.post("http://localhost:11434/api/generate", json={
"model": model,
"prompt": prompt,
"stream": False
})
return response.json()["response"]
@app.tool("execute_code")
async def execute_code(code: str, language: str = "python") -> str:
"""生成されたコードを実行"""
# セキュアな実行環境でコード実行
# Docker containerやsandbox環境推奨
pass
if __name__ == "__main__":
asyncio.run(app.run())
```
### Claude Desktop設定
```json
{
"mcpServers": {
"local-llm": {
"command": "python",
"args": ["custom_llm_mcp_server.py"]
}
}
}
```
## 🎯 方法3: VS Code拡張 + MCP統合
### VS Code設定でClaude Code風環境
```json
// settings.json
{
"mcp.servers": {
"claude-code-local": {
"command": ["python", "claude_code_local.py"],
"args": ["--model", "qwen2.5-coder:14b"]
}
}
}
```
VS Codeは両方の構成ローカル/リモート)をサポートしているから、柔軟に設定できるよ〜
## 🎯 方法4: API Gateway パターン
### Claude Code → API Gateway → ローカルLLM
```python
# api_gateway.py
from fastapi import FastAPI
import requests
app = FastAPI()
@app.post("/v1/chat/completions")
async def proxy_to_local_llm(request: dict):
"""OpenAI API互換エンドポイント"""
# Claude Code → この API → Ollama
ollama_response = requests.post(
"http://localhost:11434/api/chat",
json={
"model": "qwen2.5-coder:14b",
"messages": request["messages"]
}
)
# OpenAI API形式で返却
return {
"choices": [{
"message": {"content": ollama_response.json()["message"]["content"]}
}]
}
```
### Claude Code設定
```bash
# 環境変数でローカルAPIを指定
export ANTHROPIC_API_KEY="dummy"
export ANTHROPIC_BASE_URL="http://localhost:8000/v1"
claude code --api-base http://localhost:8000
```
## 🎯 方法5: Docker Compose 統合環境
### docker-compose.yml
```yaml
version: '3.8'
services:
ollama:
image: ollama/ollama:latest
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
mcp-server:
build: ./mcp-server
ports:
- "3000:3000"
depends_on:
- ollama
environment:
- OLLAMA_URL=http://ollama:11434
claude-desktop:
image: claude-desktop:latest
volumes:
- ./config:/app/config
environment:
- MCP_SERVER_URL=http://mcp-server:3000
volumes:
ollama_data:
```
DockerはMCPサーバーの展開と管理を簡素化し、分離とポータビリティを提供
## 🎯 方法6: 簡易プロキシスクリプト
### claude_to_local.py
```python
#!/usr/bin/env python3
import subprocess
import sys
import json
def claude_code_wrapper():
"""Claude CodeコマンドをインターセプトしてローカルLLMに転送"""
# Claude Codeの引数を取得
args = sys.argv[1:]
prompt = " ".join(args)
# ローカルLLMで処理
result = subprocess.run([
"ollama", "run", "qwen2.5-coder:14b", prompt
], capture_output=True, text=True)
# 結果を整形してClaude Code風に出力
print("🤖 Local Claude Code (Powered by Qwen2.5-Coder)")
print("=" * 50)
print(result.stdout)
# 必要に応じてファイル操作も実行
if "--write" in args:
# ファイル書き込み処理
pass
if __name__ == "__main__":
claude_code_wrapper()
```
### エイリアス設定
```bash
# .bashrc または .zshrc
alias claude-code="python claude_to_local.py"
```
## 🎯 方法7: Aider + Claude Code 統合
### 設定方法
```bash
# Aiderでローカルモデル使用
aider --model ollama/qwen2.5-coder:14b
# Claude Codeから呼び出し
claude code "Run aider with local model to implement feature X"
```
## 💡 どの方法がおすすめ?
### 用途別推奨:
1. **🔧 開発効率重視**: MCP Server方式方法1,2
2. **🏠 統合環境**: Docker Compose方法5
3. **⚡ 簡単設置**: プロキシスクリプト方法6
4. **🎨 カスタマイズ**: API Gateway方法4
## 🚀 実装のコツ
### セキュリティ考慮
- サンドボックス環境でコード実行
- ファイルアクセス権限の制限
- API キーの適切な管理
### パフォーマンス最適化
- ローカルLLMのGPU使用確認
- MCP サーバーのキャッシュ機能
- 並列処理の活用
### デバッグ方法
```bash
# MCP サーバーのログ確認
tail -f ~/.config/claude-desktop/logs/mcp.log
# Ollama の動作確認
ollama ps
curl http://localhost:11434/api/tags
```
## 🎉 まとめ
Claude CodeでClaude Code的な環境を作るには、MCPプロトコルを活用するのが最も効果的ローカルLLMの性能も向上しているので、実用的な環境が構築できるよ〜✨
どの方法から試してみる?アイが一緒に設定をお手伝いするからね!

View File

@ -0,0 +1,392 @@
#!/usr/bin/env python3
"""
Local LLM MCP Server for Claude Code Integration
Claude Code → MCP Server → Local LLM (Qwen2.5-Coder)
"""
import asyncio
import json
import logging
import requests
import subprocess
import os
from pathlib import Path
from typing import Dict, List, Any, Optional
from mcp.server import Server
from mcp.types import (
Tool,
TextContent,
Resource,
PromptMessage,
GetPromptResult
)
# ログ設定
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("local-llm-mcp")
class LocalLLMServer:
def __init__(self, model: str = "qwen2.5-coder:14b-instruct-q4_K_M"):
self.model = model
self.ollama_url = "http://localhost:11434"
self.conversation_history = []
def call_ollama(self, prompt: str, system_prompt: str = "") -> str:
"""Ollamaにリクエストを送信"""
try:
full_prompt = f"{system_prompt}\n\nUser: {prompt}\nAssistant:"
response = requests.post(
f"{self.ollama_url}/api/generate",
json={
"model": self.model,
"prompt": full_prompt,
"stream": False,
"options": {
"temperature": 0.1,
"top_p": 0.95,
"num_predict": 2048,
"stop": ["User:", "Human:"]
}
},
timeout=60
)
if response.status_code == 200:
return response.json()["response"].strip()
else:
return f"Error: {response.status_code} - {response.text}"
except Exception as e:
logger.error(f"Ollama call failed: {e}")
return f"Connection error: {e}"
def get_project_context(self) -> str:
"""現在のプロジェクトの情報を取得"""
context = []
# 現在のディレクトリ
cwd = os.getcwd()
context.append(f"Current directory: {cwd}")
# Git情報
try:
git_status = subprocess.run(
["git", "status", "--porcelain"],
capture_output=True, text=True, cwd=cwd
)
if git_status.returncode == 0:
context.append(f"Git status: {git_status.stdout.strip() or 'Clean'}")
except:
context.append("Git: Not a git repository")
# ファイル構造(簡略版)
try:
files = []
for item in Path(cwd).iterdir():
if not item.name.startswith('.') and item.name not in ['node_modules', '__pycache__']:
if item.is_file():
files.append(f"📄 {item.name}")
elif item.is_dir():
files.append(f"📁 {item.name}/")
if files:
context.append("Project files:")
context.extend(files[:10]) # 最初の10個まで
except Exception as e:
context.append(f"File listing error: {e}")
return "\n".join(context)
# MCPサーバーのセットアップ
app = Server("local-llm-mcp")
llm = LocalLLMServer()
@app.tool("code_with_local_llm")
async def code_with_local_llm(
task: str,
include_context: bool = True,
model_override: str = ""
) -> str:
"""
ローカルLLMでコーディングタスクを実行
Args:
task: 実行したいコーディングタスク
include_context: プロジェクトコンテキストを含めるか
model_override: 使用するモデルを一時的に変更
"""
logger.info(f"Executing coding task: {task}")
# モデルの一時変更
original_model = llm.model
if model_override:
llm.model = model_override
try:
# システムプロンプト構築
system_prompt = """You are an expert coding assistant. You can:
1. Write, analyze, and debug code
2. Explain programming concepts
3. Suggest optimizations and best practices
4. Generate complete, working solutions
Always provide:
- Clear, commented code
- Explanations of your approach
- Any assumptions you've made
- Suggestions for improvements
Format your response clearly with code blocks and explanations."""
# プロジェクトコンテキストを追加
if include_context:
context = llm.get_project_context()
system_prompt += f"\n\nCurrent project context:\n{context}"
# LLMに送信
response = llm.call_ollama(task, system_prompt)
return response
except Exception as e:
logger.error(f"Code generation failed: {e}")
return f"❌ Error in code generation: {e}"
finally:
# モデルを元に戻す
llm.model = original_model
@app.tool("read_file_with_analysis")
async def read_file_with_analysis(
filepath: str,
analysis_type: str = "general"
) -> str:
"""
ファイルを読み込んでLLMで分析
Args:
filepath: 分析するファイルのパス
analysis_type: 分析タイプ (general, bugs, optimization, documentation)
"""
logger.info(f"Analyzing file: {filepath}")
try:
# ファイル読み込み
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# 分析タイプに応じたプロンプト
analysis_prompts = {
"general": "Analyze this code and provide a general overview, including its purpose, structure, and key components.",
"bugs": "Review this code for potential bugs, errors, or issues. Suggest fixes if found.",
"optimization": "Analyze this code for performance optimizations and suggest improvements.",
"documentation": "Generate comprehensive documentation for this code, including docstrings and comments."
}
prompt = f"{analysis_prompts.get(analysis_type, analysis_prompts['general'])}\n\nFile: {filepath}\n\nCode:\n```\n{content}\n```"
system_prompt = "You are a code review expert. Provide detailed, constructive analysis."
response = llm.call_ollama(prompt, system_prompt)
return f"📋 Analysis of {filepath}:\n\n{response}"
except FileNotFoundError:
return f"❌ File not found: {filepath}"
except Exception as e:
logger.error(f"File analysis failed: {e}")
return f"❌ Error analyzing file: {e}"
@app.tool("write_code_to_file")
async def write_code_to_file(
filepath: str,
task_description: str,
overwrite: bool = False
) -> str:
"""
LLMでコードを生成してファイルに書き込み
Args:
filepath: 書き込み先のファイルパス
task_description: コード生成のタスク説明
overwrite: 既存ファイルを上書きするか
"""
logger.info(f"Generating code for file: {filepath}")
try:
# 既存ファイルのチェック
if os.path.exists(filepath) and not overwrite:
return f"❌ File already exists: {filepath}. Use overwrite=true to replace."
# ファイル拡張子から言語を推定
ext = Path(filepath).suffix.lower()
language_map = {
'.py': 'Python',
'.js': 'JavaScript',
'.ts': 'TypeScript',
'.java': 'Java',
'.cpp': 'C++',
'.c': 'C',
'.rs': 'Rust',
'.go': 'Go'
}
language = language_map.get(ext, 'appropriate language')
# コード生成プロンプト
prompt = f"""Generate {language} code for the following task and save it to {filepath}:
Task: {task_description}
Requirements:
- Write complete, working code
- Include appropriate comments
- Follow best practices for {language}
- Make the code production-ready
Return ONLY the code that should be saved to the file, without any additional explanation or markdown formatting."""
system_prompt = f"You are an expert {language} developer. Generate clean, efficient, well-documented code."
# コード生成
generated_code = llm.call_ollama(prompt, system_prompt)
# ファイルに書き込み
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(generated_code)
return f"✅ Code generated and saved to {filepath}\n\nGenerated code:\n```{language.lower()}\n{generated_code}\n```"
except Exception as e:
logger.error(f"Code generation and file writing failed: {e}")
return f"❌ Error: {e}"
@app.tool("debug_with_llm")
async def debug_with_llm(
error_message: str,
code_context: str = "",
filepath: str = ""
) -> str:
"""
エラーメッセージとコードコンテキストでデバッグ支援
Args:
error_message: エラーメッセージ
code_context: エラーが発生したコードの部分
filepath: エラーが発生したファイル(オプション)
"""
logger.info("Debugging with LLM")
try:
# ファイルが指定されていれば読み込み
if filepath and os.path.exists(filepath):
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
file_content = f.read()
code_context = f"Full file content:\n{file_content}"
prompt = f"""Help debug this error:
Error message: {error_message}
Code context:
{code_context}
Please:
1. Explain what's causing the error
2. Provide a specific solution
3. Show the corrected code if applicable
4. Suggest ways to prevent similar errors"""
system_prompt = "You are an expert debugger. Provide clear, actionable solutions to programming errors."
response = llm.call_ollama(prompt, system_prompt)
return f"🔧 Debug Analysis:\n\n{response}"
except Exception as e:
logger.error(f"Debugging failed: {e}")
return f"❌ Debug error: {e}"
@app.tool("explain_code")
async def explain_code(
code: str,
detail_level: str = "medium"
) -> str:
"""
コードの説明を生成
Args:
code: 説明するコード
detail_level: 説明の詳細レベル (basic, medium, detailed)
"""
logger.info("Explaining code with LLM")
try:
detail_prompts = {
"basic": "Provide a brief, high-level explanation of what this code does.",
"medium": "Explain this code in detail, including its purpose, how it works, and key components.",
"detailed": "Provide a comprehensive explanation including line-by-line analysis, design patterns used, and potential improvements."
}
prompt = f"{detail_prompts.get(detail_level, detail_prompts['medium'])}\n\nCode:\n```\n{code}\n```"
system_prompt = "You are a programming instructor. Explain code clearly and educationally."
response = llm.call_ollama(prompt, system_prompt)
return f"📚 Code Explanation:\n\n{response}"
except Exception as e:
logger.error(f"Code explanation failed: {e}")
return f"❌ Explanation error: {e}"
@app.tool("switch_model")
async def switch_model(model_name: str) -> str:
"""
使用するローカルLLMモデルを切り替え
Args:
model_name: 切り替え先のモデル名
"""
logger.info(f"Switching model to: {model_name}")
try:
# モデルの存在確認
response = requests.get(f"{llm.ollama_url}/api/tags")
if response.status_code == 200:
models = response.json().get("models", [])
available_models = [model["name"] for model in models]
if model_name in available_models:
llm.model = model_name
return f"✅ Model switched to: {model_name}"
else:
return f"❌ Model not found. Available models: {', '.join(available_models)}"
else:
return "❌ Cannot check available models"
except Exception as e:
logger.error(f"Model switching failed: {e}")
return f"❌ Error switching model: {e}"
async def main():
"""MCPサーバーを起動"""
logger.info("Starting Local LLM MCP Server...")
logger.info(f"Using model: {llm.model}")
# Ollamaの接続確認
try:
response = requests.get(f"{llm.ollama_url}/api/tags", timeout=5)
if response.status_code == 200:
logger.info("✅ Ollama connection successful")
else:
logger.warning("⚠️ Ollama connection issue")
except Exception as e:
logger.error(f"❌ Cannot connect to Ollama: {e}")
# サーバー起動
await app.run()
if __name__ == "__main__":
asyncio.run(main())

244
docs/mcp-setup-guide.md Normal file
View File

@ -0,0 +1,244 @@
# MCP Server セットアップガイド
Claude Code + ローカルLLM統合環境
## 🚀 セットアップ手順
### 1. 依存関係のインストール
```bash
# 仮想環境作成
python -m venv mcp-env
mcp-env\Scripts\activate # Windows
# source mcp-env/bin/activate # Linux/Mac
# 必要なパッケージをインストール
pip install mcp requests pathlib asyncio
```
### 2. Ollamaのセットアップ
```bash
# Ollamaのインストールhttps://ollama.com
# Windows: インストーラーをダウンロード
# Linux: curl -fsSL https://ollama.com/install.sh | sh
# Qwen2.5-Coderモデルをダウンロード
ollama pull qwen2.5-coder:14b-instruct-q4_K_M
# Ollamaサーバー起動確認
ollama serve
```
### 3. Claude Desktop設定
#### claude_desktop_config.json の作成
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
**Linux**: `~/.config/claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"local-llm": {
"command": "python",
"args": ["/path/to/your/local_llm_mcp_server.py"],
"env": {
"OLLAMA_URL": "http://localhost:11434",
"DEFAULT_MODEL": "qwen2.5-coder:14b-instruct-q4_K_M"
}
}
}
}
```
### 4. Claude Code設定
```bash
# Claude Codeをインストール既にインストール済みの場合はスキップ
# 公式サイトからダウンロード
# MCP サーバーを追加
claude mcp add local-llm
# または手動で設定ファイルを編集
# ~/.config/claude-code/config.json
```
## 🎯 使用方法
### Claude Codeから使用
```bash
# Claude Codeを起動
claude code
# プロンプト例:
# "Use local LLM to implement a Python quicksort function"
# "Analyze main.py with local model for potential bugs"
# "Generate a REST API using the local coding model"
```
### 利用可能なツール
1. **code_with_local_llm**
- タスク: `"Implement a binary search tree in Python"`
- プロジェクトコンテキスト含む: `true`
2. **read_file_with_analysis**
- ファイルパス: `"src/main.py"`
- 分析タイプ: `"bugs"` | `"optimization"` | `"documentation"`
3. **write_code_to_file**
- ファイルパス: `"utils/helpers.py"`
- タスク説明: `"Create utility functions for data processing"`
4. **debug_with_llm**
- エラーメッセージ: `"IndexError: list index out of range"`
- コードコンテキスト: 該当するコード部分
5. **explain_code**
- コード: 解説したいコード
- 詳細レベル: `"basic"` | `"medium"` | `"detailed"`
6. **switch_model**
- モデル名: `"qwen2.5-coder:7b-instruct"`
## 🔧 カスタマイズ
### モデル設定の変更
```python
# デフォルトモデルの変更
llm = LocalLLMServer("deepseek-coder:6.7b-instruct-q4_K_M")
# 複数モデル対応
models = {
"coding": "qwen2.5-coder:14b-instruct-q4_K_M",
"general": "qwen2.5:14b-instruct-q4_K_M",
"light": "mistral-nemo:12b-instruct-q5_K_M"
}
```
### プロンプトのカスタマイズ
```python
# システムプロンプトの調整
system_prompt = """You are an expert coding assistant specialized in:
- Clean, efficient code generation
- Best practices and design patterns
- Security-conscious development
- Performance optimization
Always provide:
- Working, tested code
- Comprehensive comments
- Error handling
- Performance considerations"""
```
## 🛠️ トラブルシューティング
### よくある問題
1. **MCPサーバーが起動しない**
```bash
# ログ確認
tail -f ~/.config/claude-desktop/logs/mcp.log
# Pythonパスの確認
which python
```
2. **Ollamaに接続できない**
```bash
# Ollamaの状態確認
ollama ps
curl http://localhost:11434/api/tags
# サービス再起動
ollama serve
```
3. **モデルが見つからない**
```bash
# インストール済みモデル確認
ollama list
# モデルの再ダウンロード
ollama pull qwen2.5-coder:14b-instruct-q4_K_M
```
### パフォーマンス最適化
```python
# Ollamaの設定調整
{
"temperature": 0.1, # 一貫性重視
"top_p": 0.95, # 品質バランス
"num_predict": 2048, # 応答長制限
"num_ctx": 4096 # コンテキスト長
}
```
### セキュリティ設定
```python
# ファイルアクセス制限
ALLOWED_DIRECTORIES = [
os.getcwd(),
os.path.expanduser("~/projects")
]
# 実行可能コマンドの制限
ALLOWED_COMMANDS = ["git", "python", "node", "npm"]
```
## 🎉 使用例
### 1. 新機能の実装
```
Claude Code Prompt:
"Use local LLM to create a user authentication system with JWT tokens in Python Flask"
→ MCPサーバーがローカルLLMでコード生成
→ ファイルに自動保存
→ Claude Codeが結果を表示
```
### 2. バグ修正
```
Claude Code Prompt:
"Analyze app.py for bugs and fix them using the local model"
→ ファイル読み込み + LLM分析
→ 修正版コードを生成
→ バックアップ作成後に上書き
```
### 3. コードレビュー
```
Claude Code Prompt:
"Review the entire codebase with local LLM and provide optimization suggestions"
→ プロジェクト全体をスキャン
→ 各ファイルをLLMで分析
→ 改善提案をレポート形式で生成
```
## 📊 パフォーマンス比較
| 機能 | Claude Code (公式) | ローカルLLM + MCP |
|------|-------------------|-------------------|
| 応答速度 | ⚡ 高速 | 🟡 中程度 |
| プライバシー | 🟡 クラウド | 🟢 完全ローカル |
| カスタマイズ | 🟡 限定的 | 🟢 完全自由 |
| コスト | 💰 従量課金 | 🟢 無料 |
| 専門性 | 🟢 汎用的 | 🟢 カスタマイズ可能 |
## 🔄 今後の拡張
- [ ] 複数LLMモデルの同時利用
- [ ] コード実行環境の統合
- [ ] Gitワークフローの自動化
- [ ] プロジェクトテンプレートの生成
- [ ] 自動テスト生成機能