Gmail-SAP-MCP-Assistant
Asistente MCP de Gmail + SAP
Una aplicación de Streamlit que te permite leer y gestionar Gmail, explorar datos de Business Partner / Ventas / Facturación / Productos de SAP, y hacer preguntas en lenguaje natural a un asistente de IA sobre tu bandeja de entrada (y los registros de SAP) — todo a través de un único servidor Model Context Protocol (MCP).
Streamlit UI (app.py)
│
┌───────────────┴───────────────┐
▼ ▼
Gmail / SAP / Admin tabs Ai tab
(direct button click) EmailAssistant (ai_agent.py)
│ → Mistral LLM decides which
│ tool(s) to call, if any
│ │
└───────────────┬────────────────┘
▼
MCPClient (mcp_client.py) — one persistent
background event loop for every call, either path
│
│ Streamable HTTP transport
▼
FastMCP server (server.py) — dispatches
the tool call by name
│
┌────────────────┴────────────────┐
▼ ▼
GmailService SAPService
(gmail_service.py) (sap_service.py)
│ │
▼ ▼
Gmail API SAP OData API
(OAuth credentials) (API key, sandbox)La interfaz nunca se comunica directamente con Gmail o SAP — cada acción, ya
sea un clic de botón o algo que el asistente de IA haya decidido hacer, pasa
por la misma ruta MCPClient → servidor FastMCP → capa de servicios. Esto
mantiene un único límite auditable entre la aplicación y los dos sistemas
externos.
Flujo de solicitudes
Hay dos formas en que una solicitud entra al sistema, y ambas convergen en
MCPClient.
1. Acción directa de la interfaz (pestañas Gmail / SAP / Admin)
Haces clic en un botón (p. ej. "Buscar" en la pestaña de Gmail).
app.pyllama a un método directamente en la instancia compartida deMCPClient, p. ej.mcp_client.gmail_search_messages(query, max_results).MCPClientprograma esa corrutina en su bucle de eventos dedicado en segundo plano y se bloquea hasta que se completa (_loop.run(...)enmcp_client.py) — esto es lo que mantiene la sesión MCP segura en el modelo de re-ejecución por interacción de Streamlit.El servidor FastMCP (
server.py) recibe la llamada de herramienta a través de Streamable HTTP y la envía a la función correspondiente, p. ej.gmail_search_messages().Esa función llama a
GmailService(gmail_service.py) o aSAPService(sap_service.py), que realizan la llamada de red real — la API de Gmail (OAuth) o la API OData de SAP (clave de API) — y devuelven resultados analizados en forma de diccionarios simples.El resultado regresa por la misma ruta: servidor MCP →
MCPClient._unwrap()→app.py, que lo almacena enst.session_statey lo muestra.
Los errores en cualquier capa (MCPToolError, MCPConnectionError o
cualquier cosa inesperada) se capturan de forma centralizada mediante
run_action() en app.py y se muestran como un st.error, de modo que una
llamada fallida nunca bloquea la interfaz.
2. Pregunta al asistente de IA (pestaña Ai)
Es el mismo flujo con un bucle de decisión delante:
Tu pregunta (más el historial del chat) va a
EmailAssistant.ask()enai_agent.py, que construye un prompt de sistema medianteprompts.py(con las fechas de hoy/mañana inyectadas) y lo envía a Mistral junto con el esquema deTOOLS.Mistral decide si necesita una herramienta. Si es así,
ai_agent.pyejecuta la llamada a través de la misma instancia deMCPClientque usan las pestañas manuales —EmailAssistantnunca se comunica con Gmail/SAP directamente — y devuelve el resultado JSON a la conversación.Los pasos 1–2 se repiten (buscar, luego quizás leer un mensaje concreto, luego quizás otra búsqueda) hasta
MISTRAL_MAX_STEPSveces (12 por defecto).Una vez que Mistral tiene suficiente información, deja de llamar a herramientas y devuelve una respuesta final en lenguaje natural, que
app.pymuestra en el chat — con el rastro completo de llamadas a herramientas disponible en un desplegable "Tool calls used" para depurar.
La lista de herramientas del agente es deliberadamente limitada (consultas de solo lectura de Gmail/SAP), así que no importa lo que le preguntes: no puede enviar, eliminar ni modificar nada. Solo los flujos manuales de la pestaña de Gmail pueden hacer eso, y cada uno requiere un paso de confirmación explícito antes de que se ejecute la acción destructiva.
3. Inicio de la aplicación (una vez por proceso)
get_mcp_client() en app.py está envuelto en @st.cache_resource, así que
se ejecuta exactamente una vez: crea el MCPClient, llama a .connect(),
que inicia el hilo en segundo plano + el bucle de eventos y abre la sesión
Streamable HTTP con el servidor FastMCP. Esa sesión permanece activa durante
toda la vida del proceso de Streamlit — no se recrea en cada re-ejecución.
Funciones
Gmail (mediante la pestaña "Gmail")
Listar / buscar mensajes usando la sintaxis de búsqueda nativa de Gmail
Leer un mensaje completo (cuerpo, cabeceras, adjuntos)
Enviar correos, crear borradores, mover mensajes a la Papelera (cada uno protegido por un paso de confirmación explícito en la interfaz)
Marcar mensajes como leídos/no leídos, listar etiquetas, obtener los bytes de los adjuntos
SAP (mediante la pestaña "SAP")
Comprobar la configuración / conectividad con el sandbox OData de SAP
Listar y buscar Business Partners, ver un socio individual
Listar las direcciones de correo registradas
(Solo lectura por defecto — ver Escrituras en SAP más abajo)
Asistente de IA (mediante la pestaña "Ai")
Un agente impulsado por Mistral (ai_agent.py / EmailAssistant) que
responde preguntas en lenguaje natural como:
"Resume los correos importantes de hoy"
"Busca correos que contengan adjuntos"
"¿Tengo alguna entrevista programada para mañana?"
"Resume los correos de los últimos 7 días"
El agente es intencionadamente de solo lectura: solo puede llamar a
gmail_search_messages, gmail_read_message y gmail_list_messages
(además de las consultas de solo lectura de SAP descritas en prompts.py).
No tiene ninguna herramienta para enviar, responder o eliminar nada, y el
prompt de sistema le indica explícitamente que lo diga si se le pregunta —
cada dato de sus respuestas debe poder rastrearse hasta un resultado real de
una herramienta, nunca uno inventado.
Pestaña de administración
Estado de la conexión MCP en vivo, la lista de herramientas que el servidor expone actualmente y la configuración de Gmail/SAP obtenida por última vez — útil para verificar que el servidor es accesible y está configurado correctamente.
Estructura del proyecto
Archivo | Responsabilidad |
| Interfaz de Streamlit — pestañas para IA, Gmail, SAP y Admin |
| Cliente MCP seguro para Streamlit (bucle de eventos en segundo plano persistente; ver el docstring del módulo para saber por qué) |
| Bucle del agente: envía la pregunta del usuario + los resultados de las herramientas a Mistral hasta que produce una respuesta final |
| Prompt de sistema: permisos de herramientas, contexto de fechas, referencia de sintaxis de búsqueda de Gmail, guía de estilo de respuestas |
| Servidor FastMCP que expone las operaciones de Gmail y SAP como herramientas MCP |
| Envoltorio de la API de Gmail: flujo OAuth, análisis de mensajes, operaciones de envío/borrador/eliminación/etiquetas |
| Envoltorio OData de SAP: lecturas de Business Partner, Pedido de venta, Documento de facturación, Producto (escrituras implementadas pero desactivadas por defecto) |
| Dependencias de Python |
Requisitos previos
Python 3.10+
Un proyecto de Google Cloud con la API de Gmail habilitada y un cliente OAuth de aplicación de escritorio (
credentials.json)Acceso a un sandbox OData de SAP (p. ej. el SAP Business Accelerator Hub) con una clave de API, si quieres que la pestaña/las herramientas de SAP funcionen
Una clave de API de Mistral, si quieres que la pestaña del Asistente de IA funcione
Configuración
Instalar dependencias
python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -r requirements.txtCredenciales OAuth de Gmail
Descarga el archivo
credentials.jsonde tu cliente OAuth desde Google Cloud Console y colócalo encredentials/credentials.json(o apuntaGMAIL_CREDENTIALS_FILEa otra ruta). La primera vez que el servidor llame a una herramienta de Gmail, abrirá una ventana del navegador para que autorices; el token resultante se escribe encredentials/token.json.Variables de entorno
Crea un archivo
.enven la raíz del proyecto:# --- MCP server --- MCP_SERVER_HOST=127.0.0.1 MCP_SERVER_PORT=8000 LOG_LEVEL=INFO # --- MCP client (Streamlit side) --- MCP_SERVER_URL=http://127.0.0.1:8000/mcp MCP_CONNECT_TIMEOUT_SECONDS=20 MCP_CALL_TIMEOUT_SECONDS=60 # --- Gmail --- GMAIL_CREDENTIALS_FILE=credentials/credentials.json GMAIL_TOKEN_FILE=credentials/token.json # --- SAP --- SAP_MODE=sandbox SAP_BASE_URL=https://sandbox.api.sap.com/s4hanacloud/sap/opu/odata/sap/API_BUSINESS_PARTNER SAP_API_KEY=your-sap-api-key SAP_VERIFY_SSL=true SAP_WRITE_ENABLED=false SAP_TIMEOUT_SECONDS=30 # --- AI Assistant --- MISTRAL_API_KEY=your-mistral-api-key MISTRAL_MODEL=mistral-large-latest MISTRAL_MAX_STEPS=12Todas las variables tienen un valor por defecto razonable excepto las credenciales / claves de API en sí — consulta el inicio de cada archivo
*_service.py/*_client.pypara conocer los valores de respaldo exactos.Ejecutar el servidor MCP
python server.pyEsto inicia el servidor FastMCP en
MCP_SERVER_HOST:MCP_SERVER_PORTusando el transporte Streamable HTTP.Ejecutar la aplicación de Streamlit (en una segunda terminal)
streamlit run app.pyAbre la URL que imprime Streamlit (normalmente
http://localhost:8501).
Escrituras en SAP
SAPService ya implementa create_business_partner y
update_business_partner contra los endpoints de escritura OData de SAP,
pero las herramientas MCP correspondientes están comentadas en server.py y
las llamadas están bloqueadas de forma estricta detrás de
SAP_WRITE_ENABLED=true en sap_service.py. Para habilitarlas:
Establece
SAP_WRITE_ENABLED=trueen.env.Descomenta las definiciones de las herramientas
sap_create_business_partner/sap_update_business_partnerenserver.py.Reinicia el servidor MCP.
Hasta entonces, todo el acceso a SAP es de solo lectura, independientemente de la marca.
Notas de seguridad
El Asistente de IA solo puede leer Gmail — no tiene ninguna herramienta de envío/eliminación/modificación, aplicado tanto en la lista de
TOOLSdeai_agent.pycomo en el prompt de sistema deprompts.py.Enviar correos, crear borradores y eliminar/mover a la papelera mensajes en la pestaña de Gmail requieren una casilla de confirmación explícita o un flujo de "buscar → abrir → confirmar" antes de que se ejecute la acción destructiva.
Las escrituras en SAP están desactivadas por defecto (
SAP_WRITE_ENABLED=false) y las herramientas que las realizarían ni siquiera están registradas en el servidor MCP a menos que optes por habilitarlas.
Solución de problemas
"No se pudo conectar con el servidor MCP" — asegúrate de que
server.pyse está ejecutando y de queMCP_SERVER_URLen tu.envcoincide con su host/puerto.Errores de OAuth de Gmail que mencionan un token caducado o no válido — elimina el archivo en
GMAIL_TOKEN_FILEy vuelve a intentarlo; la aplicación volverá a ejecutar el flujo OAuth.Las solicitudes a SAP fallan con errores HTTP — algunos tenants del sandbox de SAP no tienen activados todos los servicios OData (p. ej.
API_SALES_ORDER_SRV,API_BILLING_DOCUMENT_SRV) por defecto; comprueba el cuerpo del error con tu catálogo de API."El asistente de IA no está disponible" — falta
MISTRAL_API_KEYo está vacía en.env.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
MCP server exposing the Backtest360 engine API as tools for AI agents.
MCP server for Gainium — manage trading bots, deals, and balances via AI assistants
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/suryanandan1/Gmail-SAP-Connection-With-MCP-Agent'
If you have feedback or need assistance with the MCP directory API, please join our Discord server