diff --git a/src/qualtrics/LICENSE b/src/qualtrics/LICENSE new file mode 100644 index 0000000000..af1317a403 --- /dev/null +++ b/src/qualtrics/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Eder Vázquez Vázquez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/qualtrics/README.md b/src/qualtrics/README.md new file mode 100644 index 0000000000..d225ba684b --- /dev/null +++ b/src/qualtrics/README.md @@ -0,0 +1,89 @@ +# Qualtrics MCP Server + +Un servidor Model Context Protocol (MCP) para integración con la API de Qualtrics, permitiendo a los modelos de IA como Claude interactuar con encuestas, librerías, mensajes y distribuciones SMS de Qualtrics. + +## Características + +- **6 Herramientas MCP** para gestionar Qualtrics: + 1. `configurar_credenciales` - Configurar API token y data center + 2. `obtener_librerias` - Listar librerías disponibles + 3. `obtener_mensajes_libreria` - Listar mensajes de una librería + 4. `obtener_detalle_mensaje` - Obtener detalles de mensaje específico + 5. `obtener_encuestas` - Listar encuestas con estadísticas + 6. `obtener_distribuciones_sms` - Listar distribuciones SMS por encuesta + +- Cliente HTTP asíncrono con paginación automática +- Manejo robusto de errores +- Type hints completos +- Arquitectura modular + +## Instalación + +### Opción 1: Desde el código fuente + +```bash +git clone https://github.com/EderBuug/qualtrics-mcp.git +cd qualtrics-mcp +uv sync +``` + +### Opción 2: Desde PyPI (próximamente) + +```bash +pip install qualtrics-mcp +``` + +## Configuración + +### 1. Obtener credenciales de Qualtrics + +1. Inicia sesión en Qualtrics +2. Ve a **Account Settings** → **Qualtrics IDs** +3. Genera un **API Token** +4. Identifica tu **Data Center** (ej: `ca1`, `sjc1`, `fra1`) + +### 2. Configurar Claude Desktop + +Edita `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "qualtrics": { + "command": "uv", + "args": [ + "--directory", + "/path/to/qualtrics-mcp", + "run", + "python", + "main.py" + ], + "env": { + "QUALTRICS_API_TOKEN": "your_token_here", + "QUALTRICS_DATA_CENTER": "ca1" + } + } + } +} +``` + +## Uso + +Una vez configurado en Claude Desktop, podrás: + +- Gestionar librerías y mensajes de Qualtrics +- Listar y analizar encuestas +- Consultar distribuciones SMS +- Todo con procesamiento inteligente de datos + +## Repositorio + +**Código fuente completo:** https://github.com/EderBuug/qualtrics-mcp + +## Licencia + +MIT License - ver LICENSE para más detalles + +## Autor + +Eder Vázquez Vázquez (@EderBuug) diff --git a/src/qualtrics/env.example b/src/qualtrics/env.example new file mode 100644 index 0000000000..879fefdbf9 --- /dev/null +++ b/src/qualtrics/env.example @@ -0,0 +1,11 @@ +# Configuración de ejemplo para el MCP de Qualtrics +# Copia este archivo como .env y configura tus valores + +# Qualtrics API Configuration +QUALTRICS_API_TOKEN=your_api_token_here +QUALTRICS_DATA_CENTER=your_data_center_here +QUALTRICS_BASE_URL=https://your-datacenter.qualtrics.com + +# MCP Server Configuration +MCP_SERVER_NAME=qualtrics-mcp +MCP_SERVER_VERSION=0.1.0 diff --git a/src/qualtrics/main.py b/src/qualtrics/main.py new file mode 100644 index 0000000000..22058a9f50 --- /dev/null +++ b/src/qualtrics/main.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 +"""Punto de entrada principal para el MCP de Qualtrics.""" + +from src.qualtrics_mcp.server import main + +if __name__ == "__main__": + main() diff --git a/src/qualtrics/pyproject.toml b/src/qualtrics/pyproject.toml new file mode 100644 index 0000000000..e1e4f71f3d --- /dev/null +++ b/src/qualtrics/pyproject.toml @@ -0,0 +1,41 @@ +[project] +name = "qualtrics-mcp" +version = "0.1.0" +description = "Model Context Protocol (MCP) Server para integración con Qualtrics API" +readme = "README.md" +requires-python = ">=3.13" +license = {text = "MIT"} +authors = [ + {name = "Eder Vázquez Vázquez", email = "eder2v@hotmail.com"}, +] +keywords = ["mcp", "qualtrics", "api", "surveys", "model-context-protocol", "fastmcp", "claude"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.13", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Communications", +] +dependencies = [ + "fastmcp>=2.12.4", + "httpx>=0.27.0", + "pydantic>=2.0.0", + "python-dotenv>=1.0.0", + "polars>=1.0.0", +] + +[project.urls] +Homepage = "https://github.com/EderBuug/qualtrics-mcp" +Repository = "https://github.com/EderBuug/qualtrics-mcp" +Issues = "https://github.com/EderBuug/qualtrics-mcp/issues" +Documentation = "https://github.com/EderBuug/qualtrics-mcp/blob/main/README.md" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/qualtrics_mcp"] diff --git a/src/qualtrics/src/qualtrics_mcp/__init__.py b/src/qualtrics/src/qualtrics_mcp/__init__.py new file mode 100644 index 0000000000..4fe6664c28 --- /dev/null +++ b/src/qualtrics/src/qualtrics_mcp/__init__.py @@ -0,0 +1,6 @@ +"""Qualtrics MCP Server - Model Context Protocol para integración con Qualtrics API.""" + +__version__ = "0.1.0" +__author__ = "Eder Vázquez Vázquez - EderBuug" +__description__ = "MCP Server para integración con Qualtrics API" +__all__ = ["__version__", "__author__", "__description__"] diff --git a/src/qualtrics/src/qualtrics_mcp/api/__init__.py b/src/qualtrics/src/qualtrics_mcp/api/__init__.py new file mode 100644 index 0000000000..b46867f2bd --- /dev/null +++ b/src/qualtrics/src/qualtrics_mcp/api/__init__.py @@ -0,0 +1,5 @@ +"""Módulo de API para Qualtrics.""" + +from .client import QualtricsClient + +__all__ = ["QualtricsClient"] diff --git a/src/qualtrics/src/qualtrics_mcp/config.py b/src/qualtrics/src/qualtrics_mcp/config.py new file mode 100644 index 0000000000..0719f3d6b0 --- /dev/null +++ b/src/qualtrics/src/qualtrics_mcp/config.py @@ -0,0 +1,76 @@ +"""Configuración del servidor MCP de Qualtrics.""" + +import os + +from dotenv import load_dotenv + +# Cargar variables de entorno +load_dotenv() + + +class QualtricsConfig: + """Configuración para la API de Qualtrics.""" + + def __init__(self, api_token: str, data_center: str): + """ + Inicializa la configuración de Qualtrics. + + Args: + api_token: Token de API de Qualtrics + data_center: Centro de datos (ej: 'ca1', 'sjc1', 'fra1', etc.) + """ + self.api_token = api_token + self.data_center = data_center + self.base_url = f"https://{data_center}.qualtrics.com/API/v3" + self.headers = {"X-API-TOKEN": api_token, "Content-Type": "application/json"} + + def __repr__(self) -> str: + """Representación segura de la configuración (sin exponer el token).""" + return f"QualtricsConfig(data_center='{self.data_center}')" + + +# Variable global para almacenar la configuración +_qualtrics_config: QualtricsConfig | None = None + + +def get_config() -> QualtricsConfig | None: + """ + Obtiene la configuración global de Qualtrics. + + Si no está configurada, intenta cargarla desde las variables de entorno. + + Returns: + Configuración de Qualtrics o None si no está disponible + """ + global _qualtrics_config + + if _qualtrics_config is None: + api_token = os.getenv("QUALTRICS_API_TOKEN") + data_center = os.getenv("QUALTRICS_DATA_CENTER") + + if api_token and data_center: + _qualtrics_config = QualtricsConfig(api_token, data_center) + + return _qualtrics_config + + +def set_config(api_token: str, data_center: str) -> QualtricsConfig: + """ + Establece la configuración global de Qualtrics. + + Args: + api_token: Token de API de Qualtrics + data_center: Centro de datos de Qualtrics + + Returns: + Nueva configuración establecida + """ + global _qualtrics_config + _qualtrics_config = QualtricsConfig(api_token, data_center) + return _qualtrics_config + + +def clear_config() -> None: + """Limpia la configuración global.""" + global _qualtrics_config + _qualtrics_config = None diff --git a/src/qualtrics/src/qualtrics_mcp/server.py b/src/qualtrics/src/qualtrics_mcp/server.py new file mode 100644 index 0000000000..644a8089ec --- /dev/null +++ b/src/qualtrics/src/qualtrics_mcp/server.py @@ -0,0 +1,106 @@ +"""Servidor MCP principal para Qualtrics.""" + +import sys +from datetime import datetime +from typing import Any + +from fastmcp import FastMCP + +from .api.client import QualtricsClient +from .config import set_config +from .tools.distributions import listar_distribuciones_sms +from .tools.libraries import ( + listar_librerias, + listar_mensajes_libreria, + obtener_mensaje_libreria, +) +from .tools.surveys import listar_encuestas + +# Crear instancia del servidor MCP +mcp = FastMCP("qualtrics-mcp") + + +@mcp.tool() +def configurar_credenciales(api_token: str, data_center: str) -> dict[str, Any]: + """Configura las credenciales para la API de Qualtrics.""" + try: + config = set_config(api_token, data_center) + try: + with QualtricsClient(config) as client: + user_info = client.test_connection() + return { + "estado": "configurado", + "data_center": data_center, + "conexion_probada": True, + "usuario": user_info.get("result", {}).get("userId", "desconocido"), + "mensaje": "Credenciales configuradas y probadas correctamente", + "timestamp": datetime.now().isoformat(), + } + except Exception as e: + from .config import clear_config + clear_config() + return { + "estado": "error", + "mensaje": f"Error al probar conexión: {str(e)}", + "timestamp": datetime.now().isoformat(), + } + except Exception as e: + return { + "estado": "error", + "mensaje": f"Error al configurar credenciales: {str(e)}", + "timestamp": datetime.now().isoformat(), + } + + +@mcp.tool() +def obtener_librerias() -> dict[str, Any]: + """Obtiene la lista completa de librerías disponibles en Qualtrics.""" + return listar_librerias() + + +@mcp.tool() +def obtener_mensajes_libreria(library_id: str) -> dict[str, Any]: + """Obtiene todos los mensajes de una librería específica de Qualtrics.""" + return listar_mensajes_libreria(library_id) + + +@mcp.tool() +def obtener_detalle_mensaje(library_id: str, message_id: str) -> dict[str, Any]: + """Obtiene la información detallada de un mensaje específico de una librería.""" + return obtener_mensaje_libreria(library_id, message_id) + + +@mcp.tool() +def obtener_encuestas() -> dict[str, Any]: + """Obtiene la lista completa de encuestas disponibles en Qualtrics.""" + return listar_encuestas() + + +@mcp.tool() +def obtener_distribuciones_sms(survey_id: str) -> dict[str, Any]: + """Obtiene la lista de todas las distribuciones SMS para una encuesta específica.""" + return listar_distribuciones_sms(survey_id) + + +def main() -> None: + """Función principal para ejecutar el servidor MCP.""" + print("🚀 Iniciando servidor MCP Qualtrics...", file=sys.stderr) + print("", file=sys.stderr) + print("📡 Herramientas disponibles:", file=sys.stderr) + print(" 1. configurar_credenciales - Configurar API token y data center", file=sys.stderr) + print(" 2. obtener_librerias - Listar todas las librerías disponibles", file=sys.stderr) + print(" 3. obtener_mensajes_libreria - Listar mensajes de una librería", file=sys.stderr) + print(" 4. obtener_detalle_mensaje - Obtener detalles de un mensaje específico", file=sys.stderr) + print(" 5. obtener_encuestas - Listar todas las encuestas con estadísticas", file=sys.stderr) + print(" 6. obtener_distribuciones_sms - Listar distribuciones SMS por encuesta", file=sys.stderr) + print("", file=sys.stderr) + print("⏳ Esperando conexiones de clientes MCP...", file=sys.stderr) + try: + mcp.run() + except Exception as e: + print(f"💥 Error en servidor: {e}", file=sys.stderr) + raise + + +if __name__ == "__main__": + main()