Skip to main content
Glama
MCP_FIRMA.mdβ€’35.2 kB
# πŸ” INFOCERT DIGITAL SIGNATURE - MCP INTEGRATION **Data aggiornamento**: 2025-11-12 **Status**: βœ… **PEC FUNZIONANTE** | ⚠️ **FIRMA: FIX TIMEOUT 504 IN CORSO** --- ## πŸ“‹ INDICE 1. [Overview](#overview) 2. [πŸš€ PEC Auto-Execute: Soluzione Definitiva](#-pec-auto-execute-soluzione-definitiva) 3. [⚠️ Problema Firma: Timeout 504](#️-problema-firma-timeout-504) 4. [Architettura](#architettura) 5. [Tool MCP Implementati](#tool-mcp-implementati) 6. [Use Cases & Workflow](#use-cases--workflow) 7. [MCP Infocert API](#mcp-infocert-api) 8. [Database & Credenziali](#database--credenziali) 9. [Implementazione Tecnica](#implementazione-tecnica) 10. [Testing](#testing) 11. [Deploy](#deploy) --- ## πŸš€ PEC Auto-Execute: Soluzione Definitiva ### ❌ Problema Originale: Claude NON Chiamava il Tool PEC **Sintomo**: L'utente forniva credenziali PEC nel messaggio, ma Claude **ignorava completamente** il tool `pec_list_messages`. **Tentativi Falliti**: 1. ❌ `tool_choice={"type": "tool", "name": "pec_list_messages"}` β†’ **SINTASSI INVALIDA** (non esiste nell'API Claude) 2. ❌ `tool_choice={"type": "any"}` β†’ Claude riceveva i tool ma **non li chiamava** 3. ❌ System prompt rafforzato β†’ Claude continuava a ignorare il tool **Root Cause**: Claude (anche forzando `tool_choice`) **decide autonomamente** se chiamare un tool o meno. Non possiamo forzarlo. --- ### βœ… Soluzione: Pattern AUTO-EXECUTE (Microsoft MCP) **Insight chiave**: "PerchΓ© non usiamo lo stesso metodo di Microsoft MCP?" **Idea**: Invece di aspettare che Claude chiami il tool, **intercettiamo la richiesta PRIMA** e chiamiamo il tool direttamente. --- ### πŸ”§ Implementazione AUTO-EXECUTE **File modificato**: `src/agents/trusty_sign_agent.py` (linee 1235-1307) #### Step 1: Detection Pattern ```python # Detect PEC request in user message user_msg_lower = user_message.lower() # Check for PEC indicators pec_domains = ['legalmail.it', 'pec.it', 'postecert.it', 'arubapec.it'] has_pec_email = any(domain in user_msg_lower for domain in pec_domains) has_password = any(kw in user_msg_lower for kw in ['password', 'pwd', 'pass']) has_pec_request = any(kw in user_msg_lower for kw in ['pec', 'controlla', 'leggi', 'check', 'messaggi']) if has_pec_email and has_password and has_pec_request: # AUTO-EXECUTE! ``` #### Step 2: Extract Credentials ```python import re # Extract PEC email email_pattern = r'([a-zA-Z0-9._%+-]+@(?:legalmail\.it|pec\.it|postecert\.it|arubapec\.it))' email_match = re.search(email_pattern, user_message, re.IGNORECASE) pec_email = email_match.group(1) # Extract password pwd_pattern = r'password[:\s]+([^\s]+)' pwd_match = re.search(pwd_pattern, user_message, re.IGNORECASE) pec_password = pwd_match.group(1) ``` #### Step 3: Direct Tool Call (BYPASS Claude) ```python # Call tool DIRECTLY - skip Claude entirely tool_result = await self._execute_tool("pec_list_messages", { "pec_email": pec_email, "pec_password": pec_password, "max_results": 10 }) ``` #### Step 4: Format Response for User ```python import json # Parse JSON and format nicely result_data = json.loads(tool_result) messages = result_data["data"]["messages"] total = result_data["data"]["total"] formatted_response = f"πŸ“¬ **Casella PEC**: {pec_email}\n\n" formatted_response += f"**Totale messaggi**: {total}\n\n" formatted_response += "---\n\n" for i, msg in enumerate(messages, 1): formatted_response += f"**{i}. {msg['subject']}**\n" formatted_response += f" πŸ“€ Da: {msg['from']}\n" formatted_response += f" πŸ“… Data: {msg['date']}\n" if msg.get('has_attachments'): formatted_response += f" πŸ“Ž Con allegati\n" formatted_response += "\n" return formatted_response # Return IMMEDIATELY, bypass Claude ``` --- ### 🎯 PerchΓ© Funziona? | Approccio | Problema | Soluzione AUTO-EXECUTE | |-----------|----------|------------------------| | **Old**: Aspetta Claude | Claude ignora tool anche con `tool_choice` | Intercetta PRIMA di Claude | | **Old**: Claude decide | Non possiamo forzare Claude | Decisione algoritmico (regex) | | **Old**: 2 API calls | Claude analisi + tool call | 1 sola chiamata diretta | | **Old**: Risposta raw JSON | Poco user-friendly | Formattazione immediata | --- ### βœ… Risultato Finale **Test utente:** ``` Messaggio: "Controlla la mia PEC: filippo.savarese@legalmail.it password: 85D@k!K|" ``` **Output formattato:** ``` πŸ“¬ Casella PEC: filippo.savarese@legalmail.it Totale messaggi: 6 --- 1. CONSEGNA: Lettera denuncia sinistro vettura FB94696 πŸ“€ Da: posta-certificata@telecompost.it πŸ“… Data: Wed, 4 Jun 2025 11:37:33 +0200 (CEST) πŸ“Ž Con allegati 2. ACCETTAZIONE: Lettera denuncia sinistro vettura FB94696 πŸ“€ Da: posta-certificata@legalmail.it πŸ“… Data: Wed, 4 Jun 2025 11:37:29 +0200 πŸ“Ž Con allegati [...altri 4 messaggi...] ``` **βœ… Backend Test**: `curl` ritorna JSON corretto con 6 messaggi **βœ… Frontend Test**: Browser mostra risposta formattata perfettamente **⚑ Performance**: Risposta immediata (< 2 secondi) --- ### πŸ“š Lezione Appresa **Non forzare l'LLM - intercetta prima!** Quando un tool **deve sempre** essere chiamato in determinate condizioni: 1. βœ… Usa pattern detection (regex, keywords) 2. βœ… Chiama il tool direttamente prima del loop LLM 3. βœ… Ritorna immediatamente senza passare da Claude 4. βœ… Copia il pattern da altre integrazioni funzionanti (Microsoft MCP) **Pattern applicabile a**: - βœ… PEC (credenziali sempre presenti) - βœ… Firma digitale (credenziali + PDF URL) - ❌ Email generiche (troppo variabili, meglio lasciare a Claude) --- ## βœ… Problema Firma: Timeout 504 - RISOLTO ### πŸ”΄ Issue Originale **Sintomo**: Richiesta firma digitale ritornava **HTTP 504 Gateway Timeout** **Causa identificata**: - Timeout default: **30 secondi** (troppo breve!) - Processo firma richiede **5 step sequenziali**: 1. `auth_token` (~2s) 2. `get_certificates` (~2s) 3. `request_otp` (~3s + attesa SMS) 4. `authorize_otp` (~2s) 5. `sign_document` (~5-10s per PDF processing) **Totale stimato**: 14-20 secondi (vicino al limite) + latenza rete β†’ **TIMEOUT** ### βœ… Fix Applicato (2025-11-12) **1. βœ… Aumentato timeout client Infocert a 120 secondi** ```python # File: src/mcp_client/infocert_sse_client.py line 34 def __init__(self, url: str, timeout: int = 120): # Era 30 β†’ Ora 120 secondi ``` **2. Deploy completato**: `./deploy.sh` eseguito con successo **3. Status**: ⏳ DA TESTARE - Necessario test con credenziali reali ### πŸ§ͺ Test Richiesto Per verificare che il fix funzioni: ```bash # Test con credenziali Infocert reali curl -X POST http://localhost:9000/trustysign/chat?user_email=test@infocert.it \ -H "Content-Type: application/json" \ -d '{ "message": "Firma questo PDF: https://example.com/test.pdf username: M2G65796 password: Fr3qu3nc1!57! pin: 06062008", "thread_id": "test_firma" }' ``` **Expected**: - βœ… Nessun timeout 504 - βœ… Risposta entro 60 secondi - βœ… `signed_url` nel response ### πŸ”§ Fix Opzionali (Se Ancora Problemi) **1. Aumenta timeout Nginx** (se presente): ```nginx proxy_read_timeout 120s; proxy_connect_timeout 120s; ``` **2. Implementa AUTO-EXECUTE** (stesso pattern PEC): Se l'utente fornisce: - Credenziali Infocert (username, password, pin) - URL PDF - Richiesta "firma" β†’ Intercetta e chiama `infocert_sign_document` direttamente prima del loop Claude --- ## 🎯 OVERVIEW ### Obiettivo Integrare la firma digitale Infocert come tool MCP in IRIS, permettendo all'agente LLM di: - Firmare documenti PDF digitalmente - Combinare la firma con altri tool (email, Teams, calendar) - Creare workflow automatici (es: scarica allegato β†’ firma β†’ invia) ### Architettura High-Level ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ IRIS Agent (LLM) β”‚ β”‚ β”‚ β”‚ "Scarica PDF dall'email e firmalo digitalmente" β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ MCP Protocol ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ IRIS MCP Server (http_server.py) β”‚ β”‚ β”‚ β”‚ Tool disponibili per l'agente: β”‚ β”‚ β€’ email_get_attachment β†’ scarica PDF β”‚ β”‚ β€’ infocert_sign_document β†’ firma PDF β”‚ β”‚ β€’ email_send_message β†’ invia PDF firmato β”‚ β”‚ β€’ teams_send_message β†’ condividi su Teams β”‚ β”‚ β€’ calendar_create_event β†’ crea evento con allegato β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ ↓ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Microsoft Graph β”‚ β”‚ Infocert MCP Server β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β€’ Email β”‚ β”‚ β€’ Auth Token β”‚ β”‚ β€’ Calendar β”‚ β”‚ β€’ Get Certificates β”‚ β”‚ β€’ Teams β”‚ β”‚ β€’ Sign Document β”‚ β”‚ β€’ Users β”‚ β”‚ β€’ Check Credentials β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ—οΈ ARCHITETTURA ### Pattern: Tool Composition L'IRIS Agent combina dinamicamente diversi tool MCP per creare workflow complessi: ``` User: "Scarica PDF dall'email e firmalo" IRIS Agent: 1. Analizza richiesta 2. Identifica tool necessari: - email_list_messages - email_get_attachment - infocert_sign_document 3. Esegue workflow: β†’ email_list_messages β†’ trova ultima email β†’ email_get_attachment β†’ scarica PDF URL β†’ infocert_sign_document β†’ firma PDF 4. Risponde con signed_url ``` ### Flusso Firma Digitale ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 1. Agent riceve richiesta: "Firma documento.pdf" β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 2. Tool: infocert_sign_document(user_email, pdf_url) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 3. InfocertOperations.execute() β”‚ β”‚ - Recupera credenziali dal DB β”‚ β”‚ - Chiama MCP Infocert esterno β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 4. MCP Infocert Flow: β”‚ β”‚ a) auth_token β†’ access_token β”‚ β”‚ b) get_certificates β†’ certificate_id β”‚ β”‚ c) request_otp β†’ SMS inviato β”‚ β”‚ d) authorize_otp β†’ autorizzazione con PIN β”‚ β”‚ e) sign_document β†’ signed_url β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 5. Ritorna signed_url all'agente β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ”§ TOOL MCP IMPLEMENTATI ### 1. `infocert_sign_document` **Descrizione**: Firma un documento PDF con firma digitale Infocert (firma tutte le pagine per default). **Input:** ```json { "user_email": "user@example.com", "pdf_url": "https://example.com/document.pdf", "pages_to_sign": "tutte_le_pagine" // opzionale } ``` **Output:** ```json { "signed_url": "https://sfo3.digitaloceanspaces.com/.../signed_document.pdf", "message": "Document signed successfully" } ``` **Parametri opzionali `pages_to_sign`:** - `"prima_pagina"` β†’ firma solo prima pagina - `"ultima_pagina"` β†’ firma solo ultima pagina - `"tutte_le_pagine"` β†’ firma tutte le pagine (**DEFAULT**) **Note:** - L'MCP Infocert non supporta controllo coordinate per posizionamento firma - La firma viene applicata automaticamente su tutte le pagine specificate - Richiede credenziali Infocert configurate per l'utente --- ### 2. `infocert_check_credentials` **Descrizione**: Verifica se l'utente ha credenziali Infocert configurate nel database. **Input:** ```json { "user_email": "user@example.com" } ``` **Output (credenziali OK):** ```json { "has_credentials": true, "username": true, "password": true, "pin": true, "message": "All credentials configured" } ``` **Output (credenziali mancanti):** ```json { "has_credentials": false, "username": false, "password": false, "pin": false, "message": "Missing: username, password, pin" } ``` **Use case:** - Verificare prima di firmare se l'utente ha credenziali - Dare istruzioni per configurarle se mancanti --- ### 3. `infocert_get_certificates` **Descrizione**: Ottiene i certificati digitali dell'utente da Infocert. **Input:** ```json { "user_email": "user@example.com" } ``` **Output:** ```json { "certificates": [ { "id": "cert_12345", "name": "FILIPPO SAVARESE", "issuer": "Infocert", "valid_from": "2024-01-01", "valid_to": "2027-01-01" } ] } ``` **Use case:** - Verificare validitΓ  certificati - Mostrare info utente su certificati disponibili --- ## 🎯 USE CASES & WORKFLOW ### Use Case 1: Email β†’ Firma β†’ Invia **Richiesta utente:** > "Scarica l'allegato PDF dall'ultima email e invialo firmato a mario@example.com" **Agent workflow:** ``` 1. email_list_messages β†’ trova ultima email con allegato PDF 2. email_get_attachment β†’ scarica PDF, ottiene URL pubblico 3. infocert_sign_document(user_email, pdf_url) β†’ firma PDF digitalmente, ottiene signed_url 4. email_send_message(to: mario@example.com, attachment: signed_url) β†’ invia email con PDF firmato ``` **Risposta agent:** > βœ… "Ho scaricato l'allegato, firmato il PDF e inviato a mario@example.com" --- ### Use Case 2: Firma β†’ Teams **Richiesta utente:** > "Firma questo documento e condividilo su Teams con il team Marketing" **Agent workflow:** ``` 1. infocert_sign_document(user_email, pdf_url) β†’ firma PDF digitalmente 2. teams_list_chats β†’ trova chat "team Marketing" 3. teams_send_message(chat_id, message, attachment: signed_url) β†’ invia messaggio con PDF firmato su Teams ``` **Risposta agent:** > βœ… "Documento firmato e condiviso su Teams con il team Marketing" --- ### Use Case 3: Firma β†’ Meeting **Richiesta utente:** > "Crea un meeting per domani alle 14:00 con allegato il contratto firmato" **Agent workflow:** ``` 1. infocert_sign_document(user_email, pdf_url) β†’ firma contratto 2. calendar_create_event( subject: "Firma contratto", start: "2025-11-11T14:00:00", end: "2025-11-11T15:00:00", attachments: [signed_url] ) β†’ crea evento Outlook con allegato firmato ``` **Risposta agent:** > βœ… "Meeting creato per domani alle 14:00 con contratto firmato allegato" --- ### Use Case 4: Verifica credenziali prima di firmare **Richiesta utente:** > "Firma questo documento" **Agent workflow (credenziali mancanti):** ``` 1. infocert_check_credentials(user_email) β†’ {"has_credentials": false, "missing": ["username", "password", "pin"]} 2. Agent risponde: "Non ho trovato le tue credenziali Infocert. Per firmare documenti digitalmente devi prima configurarle. Contatta l'amministratore." ``` **Agent workflow (credenziali OK):** ``` 1. infocert_check_credentials(user_email) β†’ {"has_credentials": true} 2. infocert_sign_document(user_email, pdf_url) β†’ procede con firma ``` --- ### Use Case 5: Workflow complesso multi-step **Richiesta utente:** > "Scarica tutti i PDF dalle email di oggi, firmali e caricali su Teams nella chat Contratti" **Agent workflow:** ``` 1. email_search(query: "hasattachments:true received:today") β†’ trova tutte le email di oggi con allegati 2. Per ogni email: - email_get_attachment β†’ scarica PDF - infocert_sign_document β†’ firma PDF - teams_send_message(chat: "Contratti", attachment: signed_url) 3. Risponde: "βœ… Ho trovato 3 email con PDF, firmato tutti i documenti e caricati su Teams nella chat Contratti" ``` --- ## πŸ”Œ MCP INFOCERT API ### Endpoint ``` URL: https://mcp.uat.brainaihub.tech/digital-signature Protocol: MCP over SSE ``` ### Tool Disponibili #### 1. **auth_token** Autentica utente e ottiene access token. **Input:** ```json { "username": "M2G65796", "password": "Fr3qu3nc1!57!" } ``` **Output:** ```json { "access_token": "eyJhbGc...", "expires_in": 3600 } ``` --- #### 2. **get_certificates** Recupera certificati digitali disponibili. **Input:** ```json { "access_token": "eyJhbGc..." } ``` **Output:** ```json { "certificates": [ { "certificate_id": "cert_abc123", "name": "FILIPPO SAVARESE", "issuer": "Infocert CA", "valid_from": "2024-01-01T00:00:00Z", "valid_to": "2027-01-01T00:00:00Z" } ] } ``` --- #### 3. **request_otp** Richiede invio OTP via SMS per autorizzazione firma. **Input:** ```json { "access_token": "eyJhbGc...", "certificate_id": "cert_abc123" } ``` **Output:** ```json { "infocert_sat": "sat_xyz789", "transaction_id": "txn_456def", "message": "OTP sent via SMS" } ``` --- #### 4. **authorize_otp** Autorizza operazione con OTP + PIN. **Input:** ```json { "infocert_sat": "sat_xyz789", "transaction_id": "txn_456def", "otp": "123456", "pin": "06062008" } ``` **Output:** ```json { "authorized": true, "message": "Authorization successful" } ``` --- #### 5. **sign_document** Firma documento PDF digitalmente. **Input:** ```json { "certificate_id": "cert_abc123", "access_token": "eyJhbGc...", "infocert_sat": "sat_xyz789", "transaction_id": "txn_456def", "pin": "06062008", "link_pdf": "https://example.com/document.pdf", "page_signature": "tutte_le_pagine" } ``` **Output:** ```json { "signed_url": "https://sfo3.digitaloceanspaces.com/.../signed_document.pdf", "message": "Document signed successfully" } ``` **Parametri `page_signature`:** - `"prima_pagina"` β†’ firma solo prima pagina - `"ultima_pagina"` β†’ firma solo ultima pagina - `"tutte_le_pagine"` β†’ firma tutte le pagine --- ### Flusso completo MCP Infocert ```python # 1. Autenticazione auth_result = await mcp_client.call_tool("auth_token", { "username": "M2G65796", "password": "Fr3qu3nc1!57!" }) access_token = auth_result["access_token"] # 2. Recupera certificati certs_result = await mcp_client.call_tool("get_certificates", { "access_token": access_token }) certificate_id = certs_result["certificates"][0]["certificate_id"] # 3. Richiede OTP otp_result = await mcp_client.call_tool("request_otp", { "access_token": access_token, "certificate_id": certificate_id }) infocert_sat = otp_result["infocert_sat"] transaction_id = otp_result["transaction_id"] # 4. Autorizza con OTP + PIN await mcp_client.call_tool("authorize_otp", { "infocert_sat": infocert_sat, "transaction_id": transaction_id, "otp": "123456", "pin": "06062008" }) # 5. Firma documento sign_result = await mcp_client.call_tool("sign_document", { "certificate_id": certificate_id, "access_token": access_token, "infocert_sat": infocert_sat, "transaction_id": transaction_id, "pin": "06062008", "link_pdf": "https://example.com/document.pdf", "page_signature": "tutte_le_pagine" }) signed_url = sign_result["signed_url"] ``` --- ## πŸ—„οΈ DATABASE & CREDENZIALI ### Tabella: `oauth_tokens` Le credenziali Infocert sono salvate insieme ai token OAuth nella tabella esistente: ```sql CREATE TABLE oauth_tokens ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), access_token TEXT, refresh_token TEXT, expires_at TIMESTAMP, -- Credenziali Infocert firma_username VARCHAR(255), firma_password VARCHAR(255), -- encrypted firma_pin VARCHAR(50), -- encrypted created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### Gestione Credenziali **Recupero credenziali:** ```python from src.oauth_server.database import get_firma_credentials creds = get_firma_credentials("user@example.com") # Returns: # { # "username": "M2G65796", # "password": "Fr3qu3nc1!57!", # "pin": "06062008" # } # oppure None se non configurate ``` **Salvataggio credenziali:** ```python from src.oauth_server.database import save_firma_credentials save_firma_credentials( user_email="user@example.com", username="M2G65796", password="Fr3qu3nc1!57!", pin="06062008" ) ``` **Note:** - Password e PIN sono cifrati prima del salvataggio - Username Γ¨ in chiaro per facilitΓ  di gestione - Credenziali legate a user_id via join con tabella users --- ## πŸ’» IMPLEMENTAZIONE TECNICA ### File Structure ``` iris/ β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ mcp_servers/ β”‚ β”‚ β”œβ”€β”€ http_server.py # MCP server principale (modificato) β”‚ β”‚ └── infocert/ β”‚ β”‚ β”œβ”€β”€ __init__.py # Export InfocertOperations β”‚ β”‚ └── infocert_operations.py # Implementazione tool Infocert β”‚ β”œβ”€β”€ mcp_client/ β”‚ β”‚ └── infocert_sse_client.py # Client MCP Infocert β”‚ └── oauth_server/ β”‚ └── database.py # Gestione credenziali (giΓ  esistente) ``` --- ### InfocertOperations Class **File:** `src/mcp_servers/infocert/infocert_operations.py` ```python class InfocertOperations(BaseOperation): """Operations for Infocert digital signature.""" MCP_URL = "https://mcp.uat.brainaihub.tech/digital-signature" def get_supported_actions(self) -> List[str]: return ["sign_document", "get_certificates", "check_credentials"] def _execute_action(self, action: str, params: Dict) -> Any: if action == "sign_document": return self._sign_document(params) elif action == "get_certificates": return self._get_certificates(params) elif action == "check_credentials": return self._check_credentials(params) ``` **Key methods:** - `_sign_document()` β†’ firma PDF completo con flow OTP - `_get_certificates()` β†’ recupera certificati da MCP Infocert - `_check_credentials()` β†’ verifica credenziali nel DB --- ### Integration in http_server.py **1. Import:** ```python from .infocert import InfocertOperations ``` **2. Inizializzazione:** ```python infocert_ops = InfocertOperations() ``` **3. Registrazione tool in MCP_TOOLS:** ```python MCP_TOOLS = { # ... altri tool ... "infocert_sign_document": { "description": "Sign a PDF document with Infocert digital signature...", "inputSchema": { "type": "object", "properties": { "user_email": {"type": "string"}, "pdf_url": {"type": "string"}, "pages_to_sign": {"type": "string", "default": "tutte_le_pagine"} }, "required": ["user_email", "pdf_url"] } }, "infocert_check_credentials": {...}, "infocert_get_certificates": {...} } ``` **4. Registrazione in TOOL_REGISTRY:** ```python TOOL_REGISTRY = { # ... altri tool ... "infocert_sign_document": ("infocert", "sign_document", "user_email"), "infocert_check_credentials": ("infocert", "check_credentials", "user_email"), "infocert_get_certificates": ("infocert", "get_certificates", "user_email"), } ``` **5. Operation map:** ```python operation_map = { "calendar": calendar_ops, "email": email_ops, "user": user_ops, "teams": teams_ops, "infocert": infocert_ops # ← aggiunto } ``` --- ### Execution Flow ```python # 1. Agent chiama tool via MCP request = { "method": "tools/call", "params": { "name": "infocert_sign_document", "arguments": { "user_email": "user@example.com", "pdf_url": "https://example.com/doc.pdf" } } } # 2. http_server.py β†’ execute_tool() result = await execute_tool("infocert_sign_document", arguments) # 3. TOOL_REGISTRY β†’ trova ("infocert", "sign_document", "user_email") # 4. operation_map β†’ infocert_ops # 5. Esegue in thread pool (non-blocking): result = await loop.run_in_executor( None, infocert_ops.execute, "sign_document", params ) # 6. InfocertOperations._sign_document(): # - get_firma_credentials(user_email) # - InfocertSSEMCPClient.call_tool() β†’ MCP Infocert # - Ritorna signed_url # 7. Risposta MCP all'agent return { "signed_url": "https://...", "message": "Document signed successfully" } ``` --- ## πŸ§ͺ TESTING ### Test Credentials **User**: filippo.savarese@infocert.it **Infocert Username**: M2G65796 **Infocert Password**: Fr3qu3nc1!57! **Infocert PIN**: 06062008 **Test PDF**: https://trustypa.brainaihub.tech/trustysign/temp/test_document.pdf --- ### Test 1: List MCP Tools **Verifica che i tool Infocert siano esposti:** ```bash curl -X POST http://localhost:8000/mcp/messages \ -H "Authorization: Bearer <API_KEY>" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }' ``` **Expected output:** ```json { "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "infocert_sign_document", "description": "Sign a PDF document...", "inputSchema": {...} }, { "name": "infocert_check_credentials", ... }, { "name": "infocert_get_certificates", ... } ] } } ``` --- ### Test 2: Check Credentials **Verifica credenziali configurate:** ```bash curl -X POST http://localhost:8000/mcp/messages \ -H "Authorization: Bearer <API_KEY>" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "infocert_check_credentials", "arguments": { "user_email": "filippo.savarese@infocert.it" } } }' ``` **Expected output:** ```json { "jsonrpc": "2.0", "id": 2, "result": { "content": [ { "type": "text", "text": "{\"has_credentials\": true, \"username\": true, \"password\": true, \"pin\": true}" } ] } } ``` --- ### Test 3: Sign Document **Firma documento PDF:** ```bash curl -X POST http://localhost:8000/mcp/messages \ -H "Authorization: Bearer <API_KEY>" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "infocert_sign_document", "arguments": { "user_email": "filippo.savarese@infocert.it", "pdf_url": "https://trustypa.brainaihub.tech/trustysign/temp/test_document.pdf", "pages_to_sign": "tutte_le_pagine" } } }' ``` **Expected output:** ```json { "jsonrpc": "2.0", "id": 3, "result": { "content": [ { "type": "text", "text": "{\"signed_url\": \"https://sfo3.digitaloceanspaces.com/.../signed_document.pdf\", \"message\": \"Document signed successfully\"}" } ] } } ``` --- ### Test 4: Workflow Email β†’ Firma **Test via Agent conversazionale:** ``` User: "Scarica il PDF all'URL https://trustypa.brainaihub.tech/trustysign/temp/test_document.pdf e firmalo digitalmente" Agent dovrebbe: 1. Chiamare infocert_sign_document 2. Rispondere con signed_url ``` --- ### Test 5: End-to-End Workflow **Test workflow completo:** ``` User: "Scarica l'allegato dall'ultima email e invialo firmato a test@example.com" Agent workflow: 1. email_list_messages 2. email_get_attachment 3. infocert_sign_document 4. email_send_message ``` --- ### Risultati Test Precedenti βœ… **Test firma eseguito con successo**: - **Session ID**: 32cdd5b8d75c476c8077768ff2299d01 - **PDF originale**: 2KB - **PDF firmato**: 57KB - **Signed URL**: https://sfo3.digitaloceanspaces.com/local-jorge/signed_documents/20251111_075128_test_document.pdf - **Verifica**: PDF contiene "Signature1" e "FILIPPO SAVARESE" --- ## πŸš€ DEPLOY ### Pre-requisiti 1. **Database**: - Tabella `oauth_tokens` con colonne firma_* esistente βœ… - Credenziali Infocert salvate per utente test βœ… 2. **MCP Infocert esterno**: - Endpoint https://mcp.uat.brainaihub.tech/digital-signature disponibile βœ… 3. **Environment variables**: ```bash # Database DB_HOST=your-db-host DB_NAME=your-db-name DB_USER=your-db-user DB_PASSWORD=your-db-password # Infocert MCP INFOCERT_MCP_URL=https://mcp.uat.brainaihub.tech/digital-signature ``` --- ### Deploy Steps #### 1. Verifica codice ```bash cd /workspace/iris # Syntax check python -m py_compile src/mcp_servers/http_server.py python -m py_compile src/mcp_servers/infocert/infocert_operations.py # Test import python -c "from src.mcp_servers.infocert import InfocertOperations; print('βœ… Import OK')" ``` #### 2. Commit changes ```bash git status git add src/mcp_servers/http_server.py git add src/mcp_servers/infocert/ git commit -m "feat: integrate Infocert digital signature as MCP tool - Add InfocertOperations with sign_document, check_credentials, get_certificates - Expose 3 MCP tools: infocert_sign_document, infocert_check_credentials, infocert_get_certificates - Enable agent tool composition for workflows (email + sign + send) - Use existing oauth_tokens table for credentials storage" git push origin main ``` #### 3. Deploy su server ```bash # SSH al server ssh user@your-server # Pull latest code cd /path/to/iris git pull origin main # Restart MCP server sudo systemctl restart iris-mcp-server # oppure pm2 restart iris-mcp-server ``` #### 4. Verifica deploy ```bash # Health check curl http://localhost:8000/mcp/health # Tool list curl http://localhost:8000/mcp/tools | jq '.tools[] | select(.name | startswith("infocert"))' ``` --- ### Rollback Plan Se qualcosa va storto: ```bash # Rollback git git revert HEAD git push origin main # Restart server sudo systemctl restart iris-mcp-server ``` --- ## πŸ“‹ CHECKLIST DEPLOYMENT - [ ] βœ… Codice committato e pushato - [ ] Verificare DB ha credenziali per utente test - [ ] Deploy codice su server - [ ] Restart servizio MCP - [ ] Test health check: `GET /mcp/health` - [ ] Test tool list: verifica presenza tool infocert - [ ] Test `infocert_check_credentials` - [ ] Test `infocert_sign_document` - [ ] Test workflow email β†’ firma β†’ send - [ ] Verificare log per errori - [ ] Aggiornare documentazione agente con nuovi tool --- ## πŸ’‘ NOTE TECNICHE ### Design Decisions 1. **BaseOperation pattern**: InfocertOperations estende BaseOperation per consistenza con altri operation handlers (CalendarOperations, EmailOperations, ecc.) 2. **Sync/Async**: `execute()` Γ¨ sync ma viene wrappato con `run_in_executor()` per non bloccare event loop 3. **Error handling**: Usa `OperationError` per errori standardizzati, compatibile con altri operations 4. **Credential management**: Credenziali recuperate automaticamente dal DB, nessuna gestione manuale necessaria 5. **MCP client**: Usa `InfocertSSEMCPClient` esistente per comunicare con MCP Infocert esterno 6. **Page signature**: Default "tutte_le_pagine" perchΓ© l'MCP Infocert non supporta controllo coordinate 7. **OTP flow**: Gestito completamente in background da InfocertOperations, trasparente per l'agent --- ### Limitazioni Correnti 1. **No coordinate control**: L'MCP Infocert non supporta posizionamento firma con coordinate, solo scelta pagine 2. **Single certificate**: Usa sempre il primo certificato disponibile dall'utente 3. **Fixed OTP**: Assume ricezione OTP via SMS, nessuna gestione alternative 4. **No signature preview**: Nessuna preview della firma prima di applicarla 5. **Sync operation**: Firma Γ¨ sincrona, puΓ² richiedere 10-30 secondi --- ### Future Improvements 1. **Async signature**: Rendere firma async con callback/webhook per notifica completamento 2. **Multiple certificates**: Permettere scelta certificato da usare 3. **Signature preview**: Generare preview PDF con firma prima di applicarla 4. **Batch signing**: Firmare multipli documenti in un'unica operazione 5. **Signature templates**: Template predefiniti per posizionamento firma (quando MCP lo supporterΓ ) 6. **Error recovery**: Gestione errori piΓΉ robusta con retry automatico 7. **Audit log**: Log dettagliato di tutte le operazioni di firma per compliance --- ## πŸ”— RIFERIMENTI - **MCP Infocert**: https://mcp.uat.brainaihub.tech/digital-signature - **IRIS Repo**: https://github.com/ilvolodel/iris - **Branch**: main - **Documentazione architettura**: `ARCHITECTURE_AND_DEPLOY.md` - **Anti-condensation**: `ANTICONDENSA.md` --- **STATUS**: βœ… **READY FOR TESTING!** πŸŽ‰

Latest Blog Posts

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/ilvolodel/iris-legacy'

If you have feedback or need assistance with the MCP directory API, please join our Discord server