Skip to content

Commit 8d3d0c6

Browse files
committed
major release: v0.7 (industry-grade)
1 parent 287f163 commit 8d3d0c6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+17810
-228
lines changed

MCP/ragix_mcp_server.py

Lines changed: 291 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,38 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
33
"""
4-
RAGIX MCP Server
5-
================
4+
RAGIX MCP Server v0.7
5+
=====================
66
7-
Expose the Unix-RAG agent as an MCP server so that MCP clients
8-
(Claude Desktop, Cursor, OpenAI Apps, etc.) can call RAGIX as tools.
7+
Expose the RAGIX multi-agent orchestration platform as an MCP server so that
8+
MCP clients (Claude Desktop, Cursor, Claude Code, etc.) can use RAGIX tools.
99
1010
Tools exposed
1111
-------------
1212
13+
Core Tools:
1314
1. ragix_chat(prompt: str)
14-
- Run a single Unix-RAG reasoning step:
15-
* RAGIX plans
16-
* runs shell commands inside the sandbox
17-
* returns the natural-language answer and last command info.
15+
- Run a single Unix-RAG reasoning step with shell execution.
1816
1917
2. ragix_scan_repo(max_depth: int = 4, include_hidden: bool = False)
2018
- Quick project overview: list of files (path, size, extension).
2119
2220
3. ragix_read_file(path: str, max_bytes: int = 65536)
2321
- Read a file (relative to sandbox root) with a size limit.
2422
23+
v0.7 Tools:
24+
4. ragix_search(query: str, k: int = 10, strategy: str = "rrf")
25+
- Hybrid BM25 + vector search with multiple fusion strategies.
26+
27+
5. ragix_workflow(template: str, params: dict)
28+
- Execute a workflow template (bug_fix, feature_addition, etc.).
29+
30+
6. ragix_health()
31+
- Get comprehensive system health status.
32+
33+
7. ragix_templates()
34+
- List all available workflow templates.
35+
2536
Installation
2637
------------
2738
@@ -43,24 +54,29 @@
4354
UNIX_RAG_ALLOW_GIT_DESTRUCTIVE=0 # 1 to allow destructive git cmds
4455
4556
46-
Author: Olivier Vitrac | Adservio Innovation Lab | olivier.vitrac@adservio.fr
47-
Contact: olivier.vitrac@adservio.fr
57+
Author: Olivier Vitrac, PhD, HDR | olivier.vitrac@adservio.fr | Adservio | 2025-11-25
4858
4959
"""
5060

5161
from __future__ import annotations
5262

5363
from dataclasses import asdict
64+
from datetime import datetime
5465
from pathlib import Path
5566
from typing import Any, Dict, List, Optional
5667

5768
import importlib.util
69+
import json
5870
import os
71+
import sys
5972

6073
from mcp.server.fastmcp import FastMCP
6174
from mcp.server.session import ServerSession
6275
from mcp.server.fastmcp import Context
6376

77+
# Add RAGIX to path for imports
78+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
79+
6480
# ---------------------------------------------------------------------------
6581
# 1. Load the existing RAGIX Unix-RAG agent implementation dynamically
6682
# (file name has a dash so we cannot import it as a normal module).
@@ -299,7 +315,271 @@ def ragix_read_file(path: str, max_bytes: int = 65536) -> str:
299315

300316

301317
# ---------------------------------------------------------------------------
302-
# 5. Entry point
318+
# 5. RAGIX v0.7 Tools
319+
# ---------------------------------------------------------------------------
320+
321+
@mcp.tool()
322+
def ragix_search(query: str, k: int = 10, strategy: str = "rrf") -> Dict[str, Any]:
323+
"""
324+
Hybrid BM25 + vector search across the codebase.
325+
326+
Parameters
327+
----------
328+
query : str
329+
Search query (natural language or keywords).
330+
Example: "database connection error handling"
331+
332+
k : int, default 10
333+
Maximum number of results to return.
334+
335+
strategy : str, default "rrf"
336+
Fusion strategy: "rrf" (reciprocal rank), "weighted", "interleave",
337+
"bm25_rerank", or "vector_rerank".
338+
339+
Returns
340+
-------
341+
dict
342+
{
343+
"query": str,
344+
"strategy": str,
345+
"results": [
346+
{
347+
"file": str,
348+
"name": str,
349+
"type": str,
350+
"score": float,
351+
"matched_terms": list
352+
}
353+
]
354+
}
355+
"""
356+
try:
357+
from ragix_core import BM25Index, BM25Document, Tokenizer, FusionStrategy
358+
359+
# Build index from project files
360+
index = BM25Index(k1=1.5, b=0.75)
361+
tokenizer = Tokenizer(use_stopwords=True, min_token_length=2)
362+
363+
root = Path(SANDBOX_ROOT).resolve()
364+
doc_count = 0
365+
366+
# Index Python and common source files
367+
for pattern in ["**/*.py", "**/*.js", "**/*.ts", "**/*.java", "**/*.go"]:
368+
for fpath in root.glob(pattern):
369+
if fpath.is_file() and ".git" not in str(fpath):
370+
try:
371+
content = fpath.read_text(errors="replace")[:10000]
372+
tokens = tokenizer.tokenize(content)
373+
if tokens:
374+
rel_path = str(fpath.relative_to(root))
375+
index.add_document(BM25Document(
376+
doc_id=rel_path,
377+
tokens=tokens,
378+
metadata={
379+
"file": rel_path,
380+
"name": fpath.stem,
381+
"type": fpath.suffix,
382+
},
383+
))
384+
doc_count += 1
385+
except Exception:
386+
pass
387+
388+
# Search
389+
results = index.search(query, k=k)
390+
391+
formatted = []
392+
for r in results:
393+
formatted.append({
394+
"file": r.metadata.get("file", r.doc_id),
395+
"name": r.metadata.get("name", ""),
396+
"type": r.metadata.get("type", ""),
397+
"score": round(r.score, 4),
398+
"matched_terms": r.matched_terms,
399+
})
400+
401+
return {
402+
"query": query,
403+
"strategy": strategy,
404+
"indexed_files": doc_count,
405+
"results": formatted,
406+
}
407+
408+
except ImportError as e:
409+
return {"error": f"RAGIX core not available: {e}"}
410+
411+
412+
@mcp.tool()
413+
def ragix_workflow(template: str, params: Dict[str, str]) -> Dict[str, Any]:
414+
"""
415+
Execute a workflow template for multi-agent task execution.
416+
417+
Parameters
418+
----------
419+
template : str
420+
Template name: "bug_fix", "feature_addition", "code_review",
421+
"refactoring", "documentation", "security_audit", "test_coverage",
422+
or "exploration".
423+
424+
params : dict
425+
Template parameters. Each template has different required params.
426+
Use ragix_templates() to see available parameters.
427+
428+
Returns
429+
-------
430+
dict
431+
{
432+
"template": str,
433+
"status": str,
434+
"nodes": list,
435+
"execution_order": list,
436+
"params_used": dict
437+
}
438+
"""
439+
try:
440+
from ragix_core import get_template_manager
441+
442+
manager = get_template_manager()
443+
444+
# Check if template exists
445+
template_def = manager.get_template(template)
446+
if not template_def:
447+
available = list(manager.templates.keys())
448+
return {
449+
"error": f"Unknown template: {template}",
450+
"available_templates": available,
451+
}
452+
453+
# Instantiate the workflow graph
454+
graph = manager.instantiate(template, params)
455+
456+
return {
457+
"template": template,
458+
"status": "instantiated",
459+
"nodes": [
460+
{"id": n.id, "agent_type": n.agent_type, "description": n.description}
461+
for n in graph.nodes.values()
462+
],
463+
"execution_order": graph.topological_sort(),
464+
"params_used": params,
465+
}
466+
467+
except Exception as e:
468+
return {"error": str(e)}
469+
470+
471+
@mcp.tool()
472+
def ragix_health() -> Dict[str, Any]:
473+
"""
474+
Get comprehensive RAGIX system health status.
475+
476+
Returns
477+
-------
478+
dict
479+
{
480+
"status": "healthy" | "degraded" | "unhealthy",
481+
"checks": {
482+
"ollama": {...},
483+
"disk": {...},
484+
"memory": {...}
485+
},
486+
"ragix_version": str,
487+
"timestamp": str
488+
}
489+
"""
490+
try:
491+
from ragix_core import (
492+
get_health_checker,
493+
check_ollama_health,
494+
check_disk_space,
495+
check_memory_usage,
496+
)
497+
498+
checker = get_health_checker()
499+
500+
# Register checks if not already done
501+
if "ollama" not in checker.checks:
502+
checker.register("ollama", check_ollama_health)
503+
if "disk" not in checker.checks:
504+
checker.register("disk", check_disk_space)
505+
if "memory" not in checker.checks:
506+
checker.register("memory", check_memory_usage)
507+
508+
report = checker.get_status_report()
509+
510+
return {
511+
"status": report["status"],
512+
"checks": report["checks"],
513+
"ragix_version": "0.7.0",
514+
"model": OLLAMA_MODEL,
515+
"sandbox": str(SANDBOX_ROOT),
516+
"profile": AGENT_PROFILE,
517+
"timestamp": datetime.now().isoformat(),
518+
}
519+
520+
except ImportError as e:
521+
return {
522+
"status": "unknown",
523+
"error": f"RAGIX core not available: {e}",
524+
"ragix_version": "0.7.0",
525+
"timestamp": datetime.now().isoformat(),
526+
}
527+
528+
529+
@mcp.tool()
530+
def ragix_templates() -> Dict[str, Any]:
531+
"""
532+
List all available workflow templates and their parameters.
533+
534+
Returns
535+
-------
536+
dict
537+
{
538+
"templates": [
539+
{
540+
"name": str,
541+
"description": str,
542+
"parameters": [
543+
{"name": str, "required": bool, "description": str}
544+
],
545+
"steps": [str]
546+
}
547+
]
548+
}
549+
"""
550+
try:
551+
from ragix_core import get_template_manager, list_builtin_templates
552+
553+
manager = get_template_manager()
554+
templates = list_builtin_templates()
555+
556+
result = []
557+
for name in templates:
558+
template = manager.get_template(name)
559+
if template:
560+
result.append({
561+
"name": name,
562+
"description": template.description,
563+
"parameters": [
564+
{
565+
"name": p.name,
566+
"required": p.required,
567+
"default": p.default,
568+
"description": p.description,
569+
}
570+
for p in template.parameters
571+
],
572+
"steps": [s.name for s in template.steps],
573+
})
574+
575+
return {"templates": result, "count": len(result)}
576+
577+
except ImportError as e:
578+
return {"error": f"RAGIX core not available: {e}"}
579+
580+
581+
# ---------------------------------------------------------------------------
582+
# 6. Entry point
303583
# ---------------------------------------------------------------------------
304584

305585
def main() -> None:

0 commit comments

Comments
 (0)