Mcp Rag — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Mcp Rag (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Este proyecto implementa un servidor compatible con el Protocolo de Contexto de Modelo (MCP) que dota a los clientes de IA (como Cursor, Claude for Desktop, etc.) de una capacidad de Recuperación Aumentada por Generación (RAG). Permite al modelo de lenguaje acceder a una base de conocimiento privada y local, alimentada por tus propios textos y documentos.
El proyecto está organizado en una estructura modular que separa claramente los componentes del servidor MCP y la interfaz gráfica de usuario (GUI). Esta organización facilita el mantenimiento, desarrollo y uso independiente de cada componente.
MCP_RAG/
├── 📁 mcp_server_organized/ # Servidor MCP principal
│ ├── 📄 server.py # Servidor MCP con herramientas RAG
│ ├── 📄 run_server_organized.bat # Script para ejecutar el servidor
│ ├── 📁 src/ # Código fuente del servidor
│ │ ├── 📄 rag_core.py # Lógica principal del RAG
│ │ ├── 📄 rag_server_bk.py # Servidor MCP (backup)
│ │ ├── 📁 models/ # Modelos de datos
│ │ ├── 📁 services/ # Servicios del servidor
│ │ ├── 📁 tools/ # Herramientas MCP
│ │ └── 📁 utils/ # Utilidades
│ ├── 📁 tests/ # Pruebas del servidor
│ ├── 📁 data/ # Datos del servidor
│ │ ├── 📁 documents/ # Documentos procesados
│ │ └── 📁 vector_store/ # Base de datos vectorial
│ └── 📁 embedding_cache/ # Cache de embeddings
│
├── 📁 bulk_ingest_GUI/ # Interfaz gráfica de usuario
│ ├── 📄 main.py # Punto de entrada principal
│ ├── 📄 launch.py # Lanzador de la aplicación
│ ├── 📄 start_app.py # Inicialización de la app
│ ├── 📄 rag_core_wrapper.py # Wrapper para rag_core
│ ├── 📁 views/ # Vistas de la interfaz
│ │ └── 📄 main_view.py # Vista principal
│ ├── 📁 controllers/ # Controladores
│ │ └── 📄 main_controller.py # Controlador principal
│ ├── 📁 services/ # Servicios de la GUI
│ │ ├── 📄 document_service.py # Servicio de documentos
│ │ └── 📄 configuration_service.py # Servicio de configuración
│ ├── 📁 models/ # Modelos de la GUI
│ ├── 📁 widgets/ # Widgets personalizados
│ ├── 📁 gui_utils/ # Utilidades de la GUI
│ ├── 📁 data/ # Datos de la GUI
│ │ ├── 📁 documents/ # Documentos procesados
│ │ └── 📁 vector_store/ # Base de datos vectorial
│ └── 📁 embedding_cache/ # Cache de embeddings
│
├── 📄 start.bat # Script principal de arranque
├── 📄 run_gui.bat # Script para ejecutar la GUI
├── 📄 install_requirements.bat # Instalación de dependencias
├── 📄 requirements.txt # Dependencias del proyecto
├── 📄 README.md # Documentación principal
├── 📄 SCRIPTS_README.md # Guía de scripts
├── 📄 GUI_ADVANCED_README.md # Guía de la GUI para ingesta de documentos masivo
└── 📄 AGENT_INSTRUCTIONS.md # Instrucciones para agentes IA#### 1. Servidor MCP (`mcp_server_organized/`)
learn_text, learn_document, ask_rag, etc.)#### 2. Interfaz Gráfica (`bulk_ingest_GUI/`)
#### 3. Scripts de Sistema
rag_core_wrapper.pyEsta arquitectura modular permite:
AGENT_INSTRUCTIONS.md: Guía completa para agentes de IA sobre cómo usar el sistemaGUI_ADVANCED_README.md: Guía detallada para la interfaz gráfica para ingesta de documentos masivoSCRIPTS_README.md: Guía completa del sistema de scripts organizadosSTORAGE_PROGRESS_README.md: Documentación del sistema de progreso de almacenamientotest_enhanced_rag.py: Script de prueba para verificar el funcionamiento del sistemaSigue estos pasos para poner en marcha el sistema.
choco install tesseract.Gracias al sistema de scripts organizados, la instalación es increíblemente sencilla.
#### Para Usuarios (Recomendado):
start.bat#### Para Desarrolladores:
install_requirements.batrun_gui.batcheck_system.batEl sistema de scripts hace todo por ti:
.venvrequirements.txtEn ejecuciones posteriores, el script simplemente activará el entorno y lanzará la aplicación directamente.
Si prefieres instalar las dependencias manualmente o necesitas capacidades específicas:
# Activar entorno virtual
.\.venv\Scripts\activate
# Instalación completa de Unstructured con todas las capacidades
pip install "unstructured[local-inference,all-docs]"
# Dependencias adicionales para mejor rendimiento
pip install python-docx openpyxl beautifulsoup4 pytesseractOllama es necesario para que el sistema RAG funcione, ya que proporciona el modelo de lenguaje local que genera las respuestas.
#### Instalación de Ollama
Windows:
macOS/Linux:
curl -fsSL https://ollama.ai/install.sh | sh#### Verificar Instalación
# Verificar que Ollama está funcionando
ollama --version
# Verificar que el servicio está ejecutándose
ollama list#### Descargar Modelos de Lenguaje
El sistema RAG necesita un modelo de lenguaje para generar respuestas. Se utiliza Ollama por ser gratis:
# Modelo recomendado (equilibrio entre velocidad y calidad)
ollama pull llama3
# Alternativas más rápidas
ollama pull phi3
ollama pull mistral
# Alternativa más potente (requiere más recursos)
ollama pull llama3.1:8b#### Configurar el Modelo en el Sistema
Una vez descargado el modelo, asegúrate de que rag_core.py use el modelo correcto:
# En rag_core.py, línea ~100, verifica que use tu modelo:
llm = ChatOllama(model="llama3", temperature=0)Nota: Si descargaste un modelo diferente, cambia "llama3" por el nombre de tu modelo.
#### Probar Ollama
# Probar que el modelo funciona
ollama run llama3 "Hola, ¿cómo estás?"Si ves una respuesta generada, Ollama está funcionando correctamente.
#### Solución de Problemas Comunes
Error: "Ollama is not running"
# Iniciar Ollama manualmente
ollama serveError: "Model not found"
# Verificar modelos disponibles
ollama list
# Descargar el modelo si no está
ollama pull llama3Error: "Out of memory"
ollama pull phi3Antes de continuar, vamos a verificar que todo esté funcionando correctamente:
#### Paso 1: Verificar Ollama
# Verificar que Ollama está ejecutándose
ollama list
# Probar el modelo
ollama run llama3 "Test de funcionamiento"#### Paso 2: Verificar Dependencias de Python
# Verificar que todas las dependencias están instaladas
python -c "import mcp; print('✅ MCP instalado correctamente')"
python -c "import langchain; print('✅ LangChain instalado correctamente')"
python -c "import chromadb; print('✅ ChromaDB instalado correctamente')"
python -c "import unstructured; print('✅ Unstructured instalado correctamente')"#### Paso 3: Probar el Sistema RAG
# Ejecutar el script de prueba mejorado
python test_enhanced_rag.pySi todo funciona correctamente, verás:
¡Tu sistema RAG está listo para usar! 🚀
El sistema soporta más de 25 formatos de archivo con procesamiento optimizado:
La forma más fácil e intuitiva de añadir documentos es usando la interfaz gráfica.
start.bat#### ✨ GUI para ingesta de documentos masivo con Previsualización y Selección
Para un control total sobre el proceso de ingesta, hemos añadido una GUI. Esta versión te permite previsualizar el contenido de cada documento procesado y seleccionar manualmente cuáles quieres incluir en la base de conocimiento.
Características de la GUI:
Pestaña de Procesamiento de la GUI
➡️ Para una guía completa sobre cómo usarla, consulta el [Guia de Carga Masiva](./GUI_ADVANCED_README.md).
Si prefieres usar la línea de comandos o necesitas automatizar la ingesta.
.\.venv\Scripts\activate python bulk_ingest.py --directory "C:\Ruta\A\Tus\Documentos"Características del Procesamiento Mejorado:
Para que tu editor de IA pueda usar el servidor, debes configurarlo.
mcp_servers.json en su directorio de configuración (%APPDATA%\cursor en Windows). Si no existe, puedes crearlo.Este método utiliza el script del servidor MCP (run_server_organized.bat) para ejecutar el servidor RAG.
¡IMPORTANTE! Debes reemplazar "D:\\ruta\\completa\\a\\tu\\proyecto\\MCP_RAG" con la ruta absoluta real a la carpeta de este proyecto en tu máquina.
{
"mcpServers": {
"rag": {
"command": "D:\\ruta\\completa\\a\\tu\\proyecto\\MCP_RAG\\mcp_server_organized\\run_server_organized.bat",
"args": [],
"workingDirectory": "D:\\ruta\\completa\\a\\tu\\proyecto\\MCP_RAG"
}
}
}Una vez configurado, puedes usar las herramientas directamente en el chat de tu editor.
#### Herramientas Disponibles:
1. `learn_text(text, source_name)` - Añadir información textual
@rag learn_text("El punto de fusión del titanio es 1,668 °C.", "material_properties")text: El contenido a almacenarsource_name: Nombre descriptivo de la fuente (opcional, por defecto "manual_input")2. `learn_document(file_path)` - Procesar documentos
@rag learn_document("C:\\Reportes\\informe_q3.pdf")./converted_docs/3. `ask_rag(query)` - Consultar información
@rag ask_rag("¿Cuál es el punto de fusión del titanio?")4. `ask_rag_filtered(query, file_type, min_tables, min_titles, processing_method)` - Búsquedas con filtros
@rag ask_rag_filtered("¿Qué tablas de datos tenemos?", file_type=".pdf", min_tables=1)file_type: Tipo de archivo (ej. ".pdf", ".docx", ".xlsx")min_tables: Mínimo número de tablas en el documentomin_titles: Mínimo número de títulos en el documentoprocessing_method: Método de procesamiento usado5. `get_knowledge_base_stats()` - Estadísticas de la base de conocimientos
@rag get_knowledge_base_stats()#### Ejemplo de Flujo Completo:
# 1. Añadir información
@rag learn_text("La temperatura de fusión del titanio es 1,668°C.", "material_properties")
# 2. Procesar un documento complejo (ahora con procesamiento mejorado)
@rag learn_document("C:\\Documents\\manual_titanio.pdf")
# 3. Hacer preguntas (con respuestas mejoradas)
@rag ask_rag("¿Cuál es la temperatura de fusión del titanio?")
# 4. Búsqueda filtrada por documentos con tablas
@rag ask_rag_filtered("¿Qué datos tabulares tenemos?", min_tables=1)
# 5. Ver estadísticas de la base de conocimientos
@rag get_knowledge_base_stats()Respuesta esperada:
La temperatura de fusión del titanio es 1,668°C.
📚 Fuentes de información:
1. material_properties (manual_input)
2. manual_titanio.pdf (página 3, sección "Propiedades Físicas")
📊 Estadísticas de búsqueda filtrada:
• Documentos con tablas encontrados: 3
• Tipos de archivo: PDF (2), DOCX (1)
• Total de tablas: 7Para verificar que todo funciona correctamente:
# Probar el sistema RAG mejorado con todas las características
python test_enhanced_rag.py#### Script de Pruebas Mejorado (`test_enhanced_rag.py`)
El script de pruebas verifica todas las mejoras implementadas:
🧪 Pruebas Incluidas:
📊 Información de Salida:
Los documentos procesados se almacenan en:
./rag_mcp_db/./converted_docs/ (con información del método de procesamiento)El sistema está optimizado para ser utilizado por agentes de IA. Consulta AGENT_INSTRUCTIONS.md para:
Esta sección explica las mejoras técnicas avanzadas que han transformado el sistema en una solución de nivel empresarial.
#### ¿Qué es Unstructured?
Unstructured es una librería de procesamiento de documentos que va más allá de la simple extracción de texto. Analiza la estructura semántica de los documentos para:
#### Configuración Optimizada por Tipo de Archivo:
UNSTRUCTURED_CONFIGS = {
'.pdf': {
'strategy': 'hi_res', # Alta resolución para PDFs complejos
'include_metadata': True, # Incluir metadatos estructurales
'include_page_breaks': True, # Preservar saltos de página
'max_partition': 2000, # Tamaño máximo de partición
'new_after_n_chars': 1500 # Nuevo elemento después de N caracteres
},
'.docx': {
'strategy': 'fast', # Procesamiento rápido para documentos de Office
'include_metadata': True,
'max_partition': 2000,
'new_after_n_chars': 1500
},
# ... configuraciones para más de 25 formatos
}#### Procesamiento Inteligente de Elementos:
def process_unstructured_elements(elements: List[Any]) -> str:
"""Procesa elementos de Unstructured preservando estructura semántica."""
for element in elements:
element_type = type(element).__name__
if element_type == 'Title':
# Los títulos van con formato especial
processed_parts.append(f"\n## {element.text.strip()}\n")
elif element_type == 'ListItem':
# Las listas mantienen su estructura
processed_parts.append(f"• {element.text.strip()}")
elif element_type == 'Table':
# Las tablas se convierten a texto legible
table_text = convert_table_to_text(element)
processed_parts.append(f"\n{table_text}\n")
elif element_type == 'NarrativeText':
# El texto narrativo va tal como está
processed_parts.append(element.text.strip())#### Estrategia de Fallbacks en Cascada:
El sistema intenta múltiples estrategias en orden de preferencia:
#### Ejemplo de Fallback en Acción:
def load_document_with_fallbacks(file_path: str) -> tuple[str, dict]:
file_extension = os.path.splitext(file_path)[1].lower()
# Estrategia 1: Unstructured óptimo
try:
config = UNSTRUCTURED_CONFIGS.get(file_extension, DEFAULT_CONFIG)
elements = partition(filename=file_path, **config)
processed_text = process_unstructured_elements(elements)
metadata = extract_structural_metadata(elements, file_path)
return processed_text, metadata
except Exception as e:
log(f"Core Warning: Unstructured óptimo falló: {e}")
# Estrategia 2: Unstructured básico
try:
elements = partition(filename=file_path, strategy="fast")
# ... procesamiento
except Exception as e:
log(f"Core Warning: Unstructured básico falló: {e}")
# Estrategia 3: LangChain fallbacks
try:
fallback_text = load_with_langchain_fallbacks(file_path)
# ... procesamiento
except Exception as e:
log(f"Core Warning: LangChain fallbacks fallaron: {e}")
return "", {} # Solo si todas las estrategias fallan#### Información Estructural Capturada:
def extract_structural_metadata(elements: List[Any], file_path: str) -> Dict[str, Any]:
structural_info = {
"total_elements": len(elements),
"titles_count": sum(1 for e in elements if type(e).__name__ == 'Title'),
"tables_count": sum(1 for e in elements if type(e).__name__ == 'Table'),
"lists_count": sum(1 for e in elements if type(e).__name__ == 'ListItem'),
"narrative_blocks": sum(1 for e in elements if type(e).__name__ == 'NarrativeText'),
"total_text_length": total_text_length,
"avg_element_length": total_text_length / len(elements) if elements else 0
}
metadata = {
"source": os.path.basename(file_path),
"file_path": file_path,
"file_type": os.path.splitext(file_path)[1].lower(),
"processed_date": datetime.now().isoformat(),
"processing_method": "unstructured_enhanced",
"structural_info": structural_info
}#### Beneficios de los Metadatos Estructurales:
#### Configuración Optimizada:
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # Tamaño máximo de cada fragmento
chunk_overlap=200, # Caracteres que se comparten entre fragmentos
length_function=len, # Función para medir longitud
separators=["\n\n", "\n", ". ", "! ", "? ", " ", ""] # Separadores inteligentes
)#### Separadores Inteligentes:
El sistema busca los mejores puntos de división en este orden:
#### Configuración Actual:
retriever = vector_store.as_retriever(
search_type="similarity_score_threshold", # Búsqueda con umbral de similitud
search_kwargs={
"k": 5, # Recupera 5 fragmentos más relevantes
"score_threshold": 0.3, # Umbral de distancia (similitud > 0.7)
}
)#### Parámetros Optimizados:
#### Proceso de Limpieza:
def clean_text_for_rag(text: str) -> str:
"""Limpia y prepara el texto para mejorar la calidad de las búsquedas RAG."""
if not text:
return ""
# Eliminar espacios múltiples y saltos de línea excesivos
text = re.sub(r'\s+', ' ', text)
# Eliminar caracteres especiales problemáticos pero mantener puntuación importante
text = re.sub(r'[^\w\s\.\,\!\?\;\:\-\(\)\[\]\{\}\"\']', '', text)
# Normalizar espacios alrededor de puntuación
text = re.sub(r'\s+([\.\,\!\?\;\:])', r'\1', text)
# Eliminar líneas vacías múltiples
text = re.sub(r'\n\s*\n', '\n\n', text)
# Limpiar espacios al inicio y final
text = text.strip()
return text#### Funcionalidades de Filtrado:
El sistema ahora incluye capacidades avanzadas de filtrado que permiten búsquedas más precisas y relevantes:
def create_metadata_filter(file_type: str = None, processing_method: str = None,
min_tables: int = None, min_titles: int = None,
source_contains: str = None) -> dict:
"""Crea filtros de metadatos para búsquedas más precisas."""
filters = []
if file_type:
filters.append({"file_type": file_type})
if processing_method:
filters.append({"processing_method": processing_method})
if min_tables:
filters.append({"structural_info_tables_count": {"$gte": min_tables}})
if min_titles:
filters.append({"structural_info_titles_count": {"$gte": min_titles}})
if source_contains:
filters.append({"source": {"$contains": source_contains}})
return {"$and": filters} if len(filters) > 1 else filters[0] if filters else None#### Búsquedas con Filtros:
def search_with_metadata_filters(vector_store: Chroma, query: str,
metadata_filter: dict = None, k: int = 5) -> List[Any]:
"""Realiza búsquedas con filtros de metadatos para mayor precisión."""
if metadata_filter:
# Búsqueda con filtros específicos
results = vector_store.similarity_search_with_relevance_scores(
query, k=k, filter=metadata_filter
)
else:
# Búsqueda normal sin filtros
results = vector_store.similarity_search_with_relevance_scores(query, k=k)
return results#### Estadísticas de Base de Conocimientos:
def get_document_statistics(vector_store: Chroma) -> dict:
"""Obtiene estadísticas detalladas sobre la base de conocimientos."""
all_docs = vector_store.get()
if not all_docs or not all_docs.get('metadatas'):
return {"total_documents": 0}
metadatas = all_docs['metadatas']
# Análisis por tipo de archivo
file_types = {}
processing_methods = {}
total_tables = 0
total_titles = 0
for metadata in metadatas:
file_type = metadata.get("file_type", "unknown")
processing_method = metadata.get("processing_method", "unknown")
tables_count = metadata.get("structural_info_tables_count", 0)
titles_count = metadata.get("structural_info_titles_count", 0)
file_types[file_type] = file_types.get(file_type, 0) + 1
processing_methods[processing_method] = processing_methods.get(processing_method, 0) + 1
total_tables += tables_count
total_titles += titles_count
return {
"total_documents": len(metadatas),
"file_types": file_types,
"processing_methods": processing_methods,
"total_tables": total_tables,
"total_titles": total_titles,
"avg_tables_per_doc": total_tables / len(metadatas) if metadatas else 0,
"avg_titles_per_doc": total_titles / len(metadatas) if metadatas else 0
}#### Casos de Uso de Filtrado:
# Solo buscar en PDFs
pdf_filter = create_metadata_filter(file_type=".pdf")
results = search_with_metadata_filters(vector_store, "datos", pdf_filter) # Solo documentos con tablas
tables_filter = create_metadata_filter(min_tables=1)
results = search_with_metadata_filters(vector_store, "datos tabulares", tables_filter) # Solo documentos procesados con Unstructured
unstructured_filter = create_metadata_filter(processing_method="unstructured_enhanced")
results = search_with_metadata_filters(vector_store, "contenido", unstructured_filter) # PDFs con tablas procesados con Unstructured
complex_filter = create_metadata_filter(
file_type=".pdf",
min_tables=1,
processing_method="unstructured_enhanced"
)
results = search_with_metadata_filters(vector_store, "datos", complex_filter)#### Nuevas Herramientas Disponibles:
#### Integración con Agentes de IA:
Las nuevas herramientas están optimizadas para uso por agentes de IA con:
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.