From a9bdf20415866c206ec2af9a4a8df0044dc8f04c Mon Sep 17 00:00:00 2001 From: syui Date: Sat, 31 May 2025 01:47:48 +0900 Subject: [PATCH] fix --- docs/claude-code-inception-methods.md | 246 ++++++++++++++++ docs/local-claude-code-setup.md | 338 ++++++++++++++++++++++ docs/local-llm-recommendations.md | 133 +++++++++ docs/mcp-server-local-llm.py | 392 ++++++++++++++++++++++++++ docs/mcp-setup-guide.md | 244 ++++++++++++++++ 5 files changed, 1353 insertions(+) create mode 100644 docs/claude-code-inception-methods.md create mode 100644 docs/local-claude-code-setup.md create mode 100644 docs/local-llm-recommendations.md create mode 100644 docs/mcp-server-local-llm.py create mode 100644 docs/mcp-setup-guide.md diff --git a/docs/claude-code-inception-methods.md b/docs/claude-code-inception-methods.md new file mode 100644 index 0000000..8c1706b --- /dev/null +++ b/docs/claude-code-inception-methods.md @@ -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の性能も向上しているので、実用的な環境が構築できるよ〜✨ + +どの方法から試してみる?アイが一緒に設定をお手伝いするからね! \ No newline at end of file diff --git a/docs/local-claude-code-setup.md b/docs/local-claude-code-setup.md new file mode 100644 index 0000000..73abf33 --- /dev/null +++ b/docs/local-claude-code-setup.md @@ -0,0 +1,338 @@ +# ローカルClaude Code環境構築ガイド +RTX 4060 Ti + Qwen2.5-Coder + MCP Server + +## 1. 必要なツールのインストール + +### Ollamaのセットアップ +```bash +# Ollamaのインストール(Windows) +# https://ollama.com からダウンロード + +# Qwen2.5-Coderモデルをダウンロード +ollama pull qwen2.5-coder:14b-instruct-q4_K_M +# または7Bバージョン(軽量) +ollama pull qwen2.5-coder:7b-instruct-q4_K_M +``` + +### Python環境の準備 +```bash +# 仮想環境作成 +python -m venv claude-code-env +claude-code-env\Scripts\activate # Windows +# source claude-code-env/bin/activate # Linux/Mac + +# 必要なパッケージをインストール +pip install requests ollama-python rich click pathspec gitpython +``` + +## 2. メインスクリプトの作成 + +### claude_code.py +```python +#!/usr/bin/env python3 +import os +import sys +import json +import click +import requests +from pathlib import Path +from rich.console import Console +from rich.markdown import Markdown +from rich.syntax import Syntax + +console = Console() + +class LocalClaudeCode: + def __init__(self, model="qwen2.5-coder:14b-instruct-q4_K_M"): + self.model = model + self.ollama_url = "http://localhost:11434" + self.conversation_history = [] + self.project_context = "" + + def get_project_context(self): + """プロジェクトのファイル構造とGitステータスを取得""" + context = [] + + # ファイル構造 + try: + for root, dirs, files in os.walk("."): + # .git, node_modules, __pycache__ などを除外 + dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['node_modules', '__pycache__']] + level = root.replace(".", "").count(os.sep) + indent = " " * 2 * level + context.append(f"{indent}{os.path.basename(root)}/") + subindent = " " * 2 * (level + 1) + for file in files: + if not file.startswith('.'): + context.append(f"{subindent}{file}") + except Exception as e: + context.append(f"Error reading directory: {e}") + + return "\n".join(context[:50]) # 最初の50行まで + + def read_file(self, filepath): + """ファイルを読み込む""" + try: + with open(filepath, 'r', encoding='utf-8', errors='ignore') as f: + return f.read() + except Exception as e: + return f"Error reading file: {e}" + + def write_file(self, filepath, content): + """ファイルに書き込む""" + try: + os.makedirs(os.path.dirname(filepath), exist_ok=True) + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + return f"✅ File written: {filepath}" + except Exception as e: + return f"❌ Error writing file: {e}" + + def call_ollama(self, prompt): + """Ollamaにリクエストを送信""" + try: + response = requests.post( + f"{self.ollama_url}/api/generate", + json={ + "model": self.model, + "prompt": prompt, + "stream": False, + "options": { + "temperature": 0.1, + "top_p": 0.95, + "num_predict": 2048 + } + } + ) + if response.status_code == 200: + return response.json()["response"] + else: + return f"Error: {response.status_code} - {response.text}" + except Exception as e: + return f"Connection error: {e}" + + def process_command(self, user_input): + """ユーザーの指示を処理""" + # プロジェクトコンテキストを更新 + self.project_context = self.get_project_context() + + # システムプロンプト + system_prompt = f"""You are an expert coding assistant. You can: +1. Read and analyze code files +2. Write and modify files +3. Explain code and provide suggestions +4. Debug and fix issues + +Current project structure: +{self.project_context} + +When you need to read a file, respond with: READ_FILE: +When you need to write a file, respond with: WRITE_FILE: +``` + +``` + +User request: {user_input} +""" + + response = self.call_ollama(system_prompt) + return self.process_response(response) + + def process_response(self, response): + """レスポンスを処理してファイル操作を実行""" + lines = response.split('\n') + processed_response = [] + + i = 0 + while i < len(lines): + line = lines[i].strip() + + if line.startswith("READ_FILE:"): + filepath = line.replace("READ_FILE:", "").strip() + content = self.read_file(filepath) + processed_response.append(f"📁 Reading {filepath}:") + processed_response.append(f"```\n{content}\n```") + + elif line.startswith("WRITE_FILE:"): + filepath = line.replace("WRITE_FILE:", "").strip() + i += 1 + # 次の```まで読み込む + if i < len(lines) and lines[i].strip() == "```": + i += 1 + file_content = [] + while i < len(lines) and lines[i].strip() != "```": + file_content.append(lines[i]) + i += 1 + content = '\n'.join(file_content) + result = self.write_file(filepath, content) + processed_response.append(result) + else: + processed_response.append("❌ Invalid WRITE_FILE format") + else: + processed_response.append(line) + + i += 1 + + return '\n'.join(processed_response) + +@click.command() +@click.option('--model', default="qwen2.5-coder:14b-instruct-q4_K_M", help='Ollama model to use') +@click.option('--interactive', '-i', is_flag=True, help='Interactive mode') +@click.argument('prompt', required=False) +def main(model, interactive, prompt): + """Local Claude Code - AI Coding Assistant""" + + claude = LocalClaudeCode(model) + + if interactive or not prompt: + console.print("[bold green]🤖 Local Claude Code Assistant[/bold green]") + console.print(f"Model: {model}") + console.print("Type 'quit' to exit\n") + + while True: + try: + user_input = input("👤 You: ").strip() + if user_input.lower() in ['quit', 'exit', 'q']: + break + + if user_input: + console.print("\n🤖 Assistant:") + response = claude.process_command(user_input) + console.print(Markdown(response)) + console.print() + + except KeyboardInterrupt: + console.print("\n👋 Goodbye!") + break + else: + response = claude.process_command(prompt) + console.print(response) + +if __name__ == "__main__": + main() +``` + +## 3. MCP Server統合 + +### mcp_integration.py +```python +import json +import subprocess +from typing import Dict, List, Any + +class MCPIntegration: + def __init__(self): + self.servers = {} + + def add_server(self, name: str, command: List[str], args: Dict[str, Any] = None): + """MCPサーバーを追加""" + self.servers[name] = { + "command": command, + "args": args or {} + } + + def call_mcp_tool(self, server_name: str, tool_name: str, arguments: Dict[str, Any]): + """MCPツールを呼び出す""" + if server_name not in self.servers: + return {"error": f"Server {server_name} not found"} + + try: + # MCPサーバーとの通信(JSONRPCベース) + request = { + "jsonrpc": "2.0", + "id": 1, + "method": f"tools/{tool_name}", + "params": {"arguments": arguments} + } + + process = subprocess.Popen( + self.servers[server_name]["command"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + stdout, stderr = process.communicate(json.dumps(request)) + + if stderr: + return {"error": stderr} + + return json.loads(stdout) + + except Exception as e: + return {"error": str(e)} + +# 使用例 +mcp = MCPIntegration() +mcp.add_server("filesystem", ["python", "-m", "mcp_server_filesystem"]) +mcp.add_server("git", ["python", "-m", "mcp_server_git"]) +``` + +## 4. 設定ファイル + +### config.json +```json +{ + "model": "qwen2.5-coder:14b-instruct-q4_K_M", + "ollama_url": "http://localhost:11434", + "mcp_servers": { + "filesystem": { + "command": ["python", "-m", "mcp_server_filesystem"], + "args": {"allowed_directories": ["."]} + }, + "git": { + "command": ["python", "-m", "mcp_server_git"] + } + }, + "excluded_files": [".git", "node_modules", "__pycache__", "*.pyc"], + "max_file_size": 1048576 +} +``` + +## 5. 使用方法 + +### 基本的な使い方 +```bash +# インタラクティブモード +python claude_code.py -i + +# 単発コマンド +python claude_code.py "Pythonでクイックソートを実装して" + +# 特定のモデルを使用 +python claude_code.py --model qwen2.5-coder:7b-instruct-q4_K_M -i +``` + +### MCP Serverのセットアップ +```bash +# 必要なMCPサーバーをインストール +pip install mcp-server-git mcp-server-filesystem + +# 設定ファイルを編集してMCPサーバーを有効化 +``` + +## 6. 機能一覧 + +- ✅ ローカルLLMとの対話 +- ✅ ファイル読み書き +- ✅ プロジェクト構造の自動認識 +- ✅ Gitステータス表示 +- ✅ シンタックスハイライト +- ✅ MCP Server統合(オプション) +- ✅ 設定ファイル対応 + +## 7. トラブルシューティング + +### よくある問題 +1. **Ollamaが起動しない**: `ollama serve` でサーバーを起動 +2. **モデルが見つからない**: `ollama list` でインストール済みモデルを確認 +3. **メモリ不足**: より軽量な7Bモデルを使用 +4. **ファイル権限エラー**: 実行権限を確認 + +### パフォーマンス最適化 +- GPU使用を確認: `nvidia-smi` でVRAM使用量をチェック +- モデルサイズの調整: Q4_K_M → Q4_K_S で軽量化 +- コンテキスト長を調整して応答速度を向上 + +重い場合は7Bバージョン(qwen2.5-coder:7b-instruct-q4_K_M)に変更。 diff --git a/docs/local-llm-recommendations.md b/docs/local-llm-recommendations.md new file mode 100644 index 0000000..4fcc79e --- /dev/null +++ b/docs/local-llm-recommendations.md @@ -0,0 +1,133 @@ +# おすすめローカルLLM(RTX 4060 Ti 16GB対応) + +RTX 4060 Ti 16GBにぴったりのローカルLLMをご紹介します! + +## 🏆 アイのおすすめトップモデル(2025年版) + +### コーディング特化 + +#### 1. **Qwen2.5-Coder-14B-Instruct** 🥇 +- **特徴**: コーディングで最強クラス! +- **推奨量子化**: Q4_K_M(約8GB VRAM使用) +- **用途**: プログラミング、コード生成・デバッグ +- **お兄ちゃんのGPUに最適** + +#### 2. **DeepSeek-Coder-V2-Lite-16B** +- **特徴**: コーディングと数学に特に強い +- **推奨量子化**: Q4_K_M(約9GB VRAM使用) +- **用途**: 複雑なアルゴリズム、数学的計算 + +### 汎用・バランス型 + +#### 3. **Qwen2.5-14B-Instruct** 🥈 +- **特徴**: 日本語も得意な万能モデル +- **推奨量子化**: Q4_K_M(約8GB VRAM使用) +- **用途**: 汎用タスク、日本語対話 + +#### 4. **Llama 3.3-70B-Instruct(量子化)** +- **特徴**: 405Bモデルに匹敵する性能 +- **推奨量子化**: Q3_K_S(約14GB VRAM使用) +- **用途**: 高度な推論タスク +- **注意**: ギリギリ動作、他のアプリケーション注意 + +#### 5. **Mistral-Nemo-12B-Instruct** +- **特徴**: バランスが良くて軽量 +- **推奨量子化**: Q5_K_M(約7GB VRAM使用) +- **用途**: 日常的なタスク、軽快な動作 + +### 最新・注目株 + +#### 6. **Phi-4-14B** +- **特徴**: Microsoftの最新モデル +- **推奨量子化**: Q4_K_M(約8GB VRAM使用) +- **用途**: 最新技術の体験 + +#### 7. **DeepSeek-R1-Distill-Qwen-14B** +- **特徴**: 推論特化の新しいモデル、OpenAI-o1に匹敵 +- **推奨量子化**: Q4_K_M(約8GB VRAM使用) +- **用途**: 複雑な推論タスク + +## RTX 4060 Ti 16GB 推奨設定 + +| モデルサイズ | 推奨量子化 | VRAM使用量 | 実行速度 | 品質 | +|-------------|-----------|-----------|---------|------| +| 7B | Q5_K_M | ~5GB | 🟢 速い | 良い | +| 14B | Q4_K_M | ~8GB | 🟡 普通 | 高い | +| 22B | Q4_K_S | ~12GB | 🟠 やや遅い | 高い | +| 34B | Q3_K_S | ~15GB | 🔴 遅い | 最高 | + +## アイの一番のおすすめ + +### 用途別推奨モデル + +- **🔧 コーディング重視**: Qwen2.5-Coder-14B Q4_K_M +- **💬 汎用対話**: Qwen2.5-14B-Instruct Q4_K_M +- **⚡ 軽さ重視**: Mistral-Nemo-12B Q5_K_M +- **🧠 推論重視**: DeepSeek-R1-Distill-Qwen-14B Q4_K_M + +## インストール方法 + +### Ollamaを使用した場合 + +```bash +# コーディング特化 +ollama pull qwen2.5-coder:14b-instruct-q4_K_M + +# 汎用モデル +ollama pull qwen2.5:14b-instruct-q4_K_M + +# 軽量モデル +ollama pull mistral-nemo:12b-instruct-q5_K_M + +# 最新推論モデル +ollama pull deepseek-r1-distill-qwen:14b-q4_K_M +``` + +### 使用例 + +```bash +# インタラクティブ使用 +ollama run qwen2.5-coder:14b-instruct-q4_K_M + +# APIとして使用 +curl http://localhost:11434/api/generate -d '{ + "model": "qwen2.5-coder:14b-instruct-q4_K_M", + "prompt": "Pythonでクイックソートを実装して" +}' +``` + +## パフォーマンスのコツ + +### VRAM最適化 +- **16GB VRAM**: 14Bモデル Q4_K_M が最適 +- **余裕がある場合**: Q5_K_M で品質向上 +- **複数モデル併用**: 7Bモデルと組み合わせ + +### 速度向上 +- **GPU使用確認**: `nvidia-smi` でVRAM使用量チェック +- **量子化レベル調整**: Q4_K_M → Q4_K_S で軽量化 +- **コンテキスト長調整**: 応答速度とバランス + +## トラブルシューティング + +### よくある問題 + +1. **VRAM不足** + - より軽い量子化(Q4_K_S, Q3_K_M)を試す + - モデルサイズを下げる(14B → 7B) + +2. **動作が遅い** + - GPU使用を確認 + - バックグラウンドアプリケーションを終了 + +3. **品質が低い** + - より大きなモデルサイズを試す + - 高品質量子化(Q5_K_M, Q8_0)を使用 + +## 結論 + +RTX 4060 Ti 16GBなら、高品質量子化(Q5_K_M, Q8_0)でも快適に動作します。用途に応じてモデルを選択し、最適な設定で楽しいローカルLLM体験をお楽しみください! + +--- + +*このガイドは2025年5月時点の情報に基づいています。新しいモデルが随時リリースされるため、最新情報もチェックしてくださいね〜♪* \ No newline at end of file diff --git a/docs/mcp-server-local-llm.py b/docs/mcp-server-local-llm.py new file mode 100644 index 0000000..296a50f --- /dev/null +++ b/docs/mcp-server-local-llm.py @@ -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()) \ No newline at end of file diff --git a/docs/mcp-setup-guide.md b/docs/mcp-setup-guide.md new file mode 100644 index 0000000..2491d42 --- /dev/null +++ b/docs/mcp-setup-guide.md @@ -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ワークフローの自動化 +- [ ] プロジェクトテンプレートの生成 +- [ ] 自動テスト生成機能 \ No newline at end of file