* feat: Add Ollama embedding support for local embedding models * docs: Add clear documentation for Ollama embedding usage * fix: remove leann_ask * docs: remove ollama embedding extra instructions * simplify MCP interface for Claude Code - Remove unnecessary search parameters: search_mode, recompute_embeddings, file_types, min_score - Remove leann_clear tool (not needed for Claude Code workflow) - Streamline search to only use: query, index_name, top_k, complexity - Keep core tools: leann_index, leann_search, leann_status, leann_list 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * remove leann_index from MCP interface Users should use CLI command 'leann build' to create indexes first. MCP now only provides search functionality: - leann_search: search existing indexes - leann_status: check index health - leann_list: list available indexes This separates index creation (CLI) from search (Claude Code). 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * improve CLI with auto project name and .gitignore support - Make index_name optional, auto-use current directory name - Read .gitignore patterns and respect them during indexing - Add _read_gitignore_patterns() to parse .gitignore files - Add _should_exclude_file() for pattern matching - Apply exclusion patterns to both PDF and general file processing - Show helpful messages about gitignore usage Now users can simply run: leann build And it will use project name + respect .gitignore patterns. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
177 lines
7.1 KiB
Python
Executable File
177 lines
7.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
def handle_request(request):
|
|
if request.get("method") == "initialize":
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": request.get("id"),
|
|
"result": {
|
|
"capabilities": {"tools": {}},
|
|
"protocolVersion": "2024-11-05",
|
|
"serverInfo": {"name": "leann-mcp", "version": "1.0.0"},
|
|
},
|
|
}
|
|
|
|
elif request.get("method") == "tools/list":
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": request.get("id"),
|
|
"result": {
|
|
"tools": [
|
|
{
|
|
"name": "leann_search",
|
|
"description": """🔍 Search code using natural language - like having a coding assistant who knows your entire codebase!
|
|
|
|
🎯 **Perfect for**:
|
|
- "How does authentication work?" → finds auth-related code
|
|
- "Error handling patterns" → locates try-catch blocks and error logic
|
|
- "Database connection setup" → finds DB initialization code
|
|
- "API endpoint definitions" → locates route handlers
|
|
- "Configuration management" → finds config files and usage
|
|
|
|
💡 **Pro tip**: Use this before making any changes to understand existing patterns and conventions.""",
|
|
"inputSchema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"index_name": {
|
|
"type": "string",
|
|
"description": "Name of the LEANN index to search. Use 'leann_list' first to see available indexes.",
|
|
},
|
|
"query": {
|
|
"type": "string",
|
|
"description": "Search query - can be natural language (e.g., 'how to handle errors') or technical terms (e.g., 'async function definition')",
|
|
},
|
|
"top_k": {
|
|
"type": "integer",
|
|
"default": 5,
|
|
"minimum": 1,
|
|
"maximum": 20,
|
|
"description": "Number of search results to return. Use 5-10 for focused results, 15-20 for comprehensive exploration.",
|
|
},
|
|
"complexity": {
|
|
"type": "integer",
|
|
"default": 32,
|
|
"minimum": 16,
|
|
"maximum": 128,
|
|
"description": "Search complexity level. Use 16-32 for fast searches (recommended), 64+ for higher precision when needed.",
|
|
},
|
|
},
|
|
"required": ["index_name", "query"],
|
|
},
|
|
},
|
|
{
|
|
"name": "leann_status",
|
|
"description": "📊 Check the health and stats of your code indexes - like a medical checkup for your codebase knowledge!",
|
|
"inputSchema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"index_name": {
|
|
"type": "string",
|
|
"description": "Optional: Name of specific index to check. If not provided, shows status of all indexes.",
|
|
}
|
|
},
|
|
},
|
|
},
|
|
{
|
|
"name": "leann_list",
|
|
"description": "📋 Show all your indexed codebases - your personal code library! Use this to see what's available for search.",
|
|
"inputSchema": {"type": "object", "properties": {}},
|
|
},
|
|
]
|
|
},
|
|
}
|
|
|
|
elif request.get("method") == "tools/call":
|
|
tool_name = request["params"]["name"]
|
|
args = request["params"].get("arguments", {})
|
|
|
|
try:
|
|
if tool_name == "leann_search":
|
|
# Validate required parameters
|
|
if not args.get("index_name") or not args.get("query"):
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": request.get("id"),
|
|
"result": {
|
|
"content": [
|
|
{
|
|
"type": "text",
|
|
"text": "Error: Both index_name and query are required",
|
|
}
|
|
]
|
|
},
|
|
}
|
|
|
|
# Build simplified command
|
|
cmd = [
|
|
"leann",
|
|
"search",
|
|
args["index_name"],
|
|
args["query"],
|
|
f"--top-k={args.get('top_k', 5)}",
|
|
f"--complexity={args.get('complexity', 32)}",
|
|
]
|
|
|
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
|
|
elif tool_name == "leann_status":
|
|
if args.get("index_name"):
|
|
# Check specific index status - for now, we'll use leann list and filter
|
|
result = subprocess.run(["leann", "list"], capture_output=True, text=True)
|
|
# We could enhance this to show more detailed status per index
|
|
else:
|
|
# Show all indexes status
|
|
result = subprocess.run(["leann", "list"], capture_output=True, text=True)
|
|
|
|
elif tool_name == "leann_list":
|
|
result = subprocess.run(["leann", "list"], capture_output=True, text=True)
|
|
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": request.get("id"),
|
|
"result": {
|
|
"content": [
|
|
{
|
|
"type": "text",
|
|
"text": result.stdout
|
|
if result.returncode == 0
|
|
else f"Error: {result.stderr}",
|
|
}
|
|
]
|
|
},
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
"jsonrpc": "2.0",
|
|
"id": request.get("id"),
|
|
"error": {"code": -1, "message": str(e)},
|
|
}
|
|
|
|
|
|
def main():
|
|
for line in sys.stdin:
|
|
try:
|
|
request = json.loads(line.strip())
|
|
response = handle_request(request)
|
|
if response:
|
|
print(json.dumps(response))
|
|
sys.stdout.flush()
|
|
except Exception as e:
|
|
error_response = {
|
|
"jsonrpc": "2.0",
|
|
"id": None,
|
|
"error": {"code": -1, "message": str(e)},
|
|
}
|
|
print(json.dumps(error_response))
|
|
sys.stdout.flush()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|