Skip to main content
Glama
PEC_MCP.mdβ€’28.1 kB
# πŸ“§ PEC (POSTA ELETTRONICA CERTIFICATA) - MCP INTEGRATION **Data aggiornamento**: 2025-11-24 **Status**: ⚠️ **OUTPUT SIZE ISSUE - SOLUZIONE IDENTIFICATA** --- ## πŸ“‹ INDICE 1. [Overview](#overview) 2. [Architettura](#architettura) 3. [Tool MCP Implementati](#tool-mcp-implementati) 4. [Use Cases & Workflow](#use-cases--workflow) 5. [Sbustamento PEC (Unwrapping)](#sbustamento-pec-unwrapping) 6. [Provider PEC Supportati](#provider-pec-supportati) 7. [Implementazione Tecnica](#implementazione-tecnica) 8. [Testing](#testing) --- ## 🎯 OVERVIEW ### Obiettivo Integrare la gestione di **PEC (Posta Elettronica Certificata)** come tool MCP in IRIS, permettendo all'agente LLM di: - Leggere email PEC da IMAP con **sbustamento automatico** (estrazione da postacert.eml) - Estrarre allegati del messaggio originale - Inviare PEC via SMTP - Gestire metadati di certificazione (daticert.xml) ### Architettura High-Level ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ IRIS Agent (LLM) β”‚ β”‚ β”‚ β”‚ "Controlla la mia PEC e dimmi quali allegati ho ricevuto" β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ MCP Protocol ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ IRIS MCP Server (http_server.py) β”‚ β”‚ β”‚ β”‚ Tool disponibili per l'agente: β”‚ β”‚ β€’ pec_list_messages β†’ lista PEC con sbustamento β”‚ β”‚ β€’ pec_get_message β†’ legge singola PEC con unwrapping β”‚ β”‚ β€’ pec_get_attachment β†’ estrae allegato da PEC β”‚ β”‚ β€’ pec_send_message β†’ invia PEC via SMTP β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ PECOperations (Internal) β”‚ β”‚ β”‚ β”‚ β€’ IMAP Client (imaplib) β”‚ β”‚ β€’ SMTP Client (smtplib) β”‚ β”‚ β€’ PECUnwrapper (sbustamento postacert.eml) β”‚ β”‚ β€’ Provider Auto-detection (Legalmail, Aruba, PosteCert) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Differenza con Infocert | Aspetto | Infocert MCP | PEC MCP | |---------|--------------|---------| | **Architettura** | Server SSE esterno | Operazioni interne (come Microsoft Graph) | | **Protocollo** | SSE + JSON-RPC | Diretto (IMAP/SMTP Python) | | **Servizio** | Terze parti (UAT server) | Interno IRIS | | **Credenziali** | Username/password/PIN conversazionale | Email/password conversazionale | --- ## πŸ—οΈ ARCHITETTURA ### Pattern: Internal Operations PEC segue il pattern di **Microsoft Graph Operations**, non quello di Infocert SSE: ``` User Request ↓ IRIS Agent ↓ http_server.py (tool dispatcher) ↓ PECOperations.execute(action, params) ↓ IMAP/SMTP + PECUnwrapper ↓ Result (unwrapped PEC data) ``` ### Flusso Lettura PEC ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 1. Agent riceve richiesta: "Controlla la mia PEC" β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 2. Agent chiede: "Fornisci email e password PEC" β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 3. User fornisce: β”‚ β”‚ - pec_email: filippo.savarese@legalmail.it β”‚ β”‚ - pec_password: ******** β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 4. Tool: pec_list_messages β”‚ β”‚ β†’ Auto-detect provider (Legalmail) β”‚ β”‚ β†’ Connect IMAP (mbox.cert.legalmail.it:993) β”‚ β”‚ β†’ List messages β”‚ β”‚ β†’ Check is_pec_message() per ogni email β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 5. Per ogni PEC trovata: β”‚ β”‚ β†’ unwrap_pec() automatico β”‚ β”‚ β†’ Estrae postacert.eml (messaggio originale) β”‚ β”‚ β†’ Estrae daticert.xml (metadati certificazione) β”‚ β”‚ β†’ Lista allegati del messaggio originale β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 6. Ritorna risultato con original_message unwrapped β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ”§ TOOL MCP IMPLEMENTATI ### 1. `pec_list_messages` **Descrizione**: Elenca messaggi PEC da INBOX con sbustamento automatico. **Input:** ```json { "pec_email": "filippo.savarese@legalmail.it", "pec_password": "mypassword", "folder": "INBOX", "max_results": 10 } ``` **Output:** ```json { "messages": [ { "id": "123", "subject": "CONSEGNA: Lettera denuncia sinistro", "from": "posta-certificata@telecompost.it", "to": "filippo.savarese@legalmail.it", "date": "Wed, 4 Jun 2025 11:37:33 +0200", "is_pec": true, "pec_type": "delivery", "has_attachments": true } ], "total": 6 } ``` **Note:** - Auto-detection provider dal dominio email (@legalmail.it β†’ Legalmail) - `is_pec` = true se contiene postacert.eml e daticert.xml - `pec_type`: "normal", "delivery", "acceptance", "error" --- ### 2. `pec_get_message` **Descrizione**: Ottiene messaggio singolo con **sbustamento automatico completo**. **Input:** ```json { "pec_email": "filippo.savarese@legalmail.it", "pec_password": "mypassword", "message_id": "123" } ``` **Output:** ```json { "is_pec": true, "pec_type": "delivery", "container": { "from": "posta-certificata@telecompost.it", "to": "filippo.savarese@legalmail.it", "subject": "CONSEGNA: Lettera denuncia sinistro", "date": "Wed, 4 Jun 2025 11:37:33 +0200", "message_id": "<abc123@postacert>" }, "original_message": { "from": "sender@example.com", "to": "filippo.savarese@legalmail.it", "subject": "Lettera denuncia sinistro vettura", "date": "Wed, 4 Jun 2025 10:00:00 +0200", "message_id": "<original123@sender>", "body_text": "Contenuto del messaggio originale...", "body_html": "<html>...", "attachments": [ { "filename": "denuncia_sinistro.pdf", "content_type": "application/pdf", "size": 245632, "data": "base64..." } ] }, "daticert": "<?xml version=\"1.0\"?>\n<postacert>...", "metadata": { "certification_type": "delivery", "date": "2025-06-04T11:37:33+02:00", "sender": "sender@example.com", "recipient": "filippo.savarese@legalmail.it" } } ``` **Flusso Unwrapping:** 1. Scarica messaggio completo via IMAP 2. Parse container email 3. Cerca allegato `postacert.eml` 4. Estrae e parsa messaggio originale da postacert.eml 5. Estrae allegati del messaggio originale (esclusi file sistema PEC) 6. Cerca e parsa `daticert.xml` per metadati certificazione 7. Ritorna struttura completa unwrapped **File Sistema PEC esclusi dagli allegati:** - `postacert.eml` (messaggio originale unwrapped) - `daticert.xml` (metadati certificazione) - `smime.p7s` / `smime.p7m` (firma digitale) --- ### 3. `pec_get_attachment` **Descrizione**: Estrae allegato specifico da messaggio PEC unwrapped. **Input:** ```json { "pec_email": "filippo.savarese@legalmail.it", "pec_password": "mypassword", "message_id": "123", "attachment_index": 0 } ``` **Output:** ```json { "filename": "denuncia_sinistro.pdf", "content_type": "application/pdf", "size": 245632, "data": "JVBERi0xLjQKJeLjz9MKMyAwIG9..." } ``` **Note:** - `attachment_index` Γ¨ 0-based - Ritorna attachment del **messaggio originale unwrapped**, non del container - `data` Γ¨ base64 encoded --- ### 4. `pec_send_message` **Descrizione**: Invia PEC via SMTP. **Input:** ```json { "pec_email": "filippo.savarese@legalmail.it", "pec_password": "mypassword", "to": "recipient@example.com", "subject": "Notifica importante", "body": "Testo del messaggio...", "attachments": [ { "filename": "documento.pdf", "data": "base64..." } ] } ``` **Output:** ```json { "success": true, "message": "Email sent successfully" } ``` --- ## 🎯 USE CASES & WORKFLOW ### Use Case 1: Lettura PEC con estrazione allegati **Richiesta utente:** > "Controlla la mia PEC e dimmi quali allegati ho ricevuto oggi" **Agent workflow:** ``` 1. Agent chiede credenziali PEC: "Per accedere alla tua PEC, fornisci:" - Email PEC - Password 2. User fornisce: filippo.savarese@legalmail.it / ******** 3. pec_list_messages(pec_email, pec_password, max_results=10) β†’ trova 6 messaggi, 4 sono PEC 4. Per ogni PEC con has_attachments=true: pec_get_message(message_id) β†’ unwrap automatico, estrae original_message 5. Agent risponde: "πŸ“§ Hai ricevuto 4 PEC oggi: 1. Da: sender@example.com Oggetto: Lettera denuncia sinistro Allegati: - denuncia_sinistro.pdf (240 KB) - foto_danno.jpg (1.2 MB) 2. Da: company@pec.it Oggetto: Fattura n. 12345 Allegati: - fattura_12345.pdf (85 KB)" ``` --- ### Use Case 2: Scarica PDF da PEC e firma digitalmente **Richiesta utente:** > "Scarica il PDF dalla mia ultima PEC, firmalo con Infocert e rimandalo" **Agent workflow:** ``` 1. Chiede credenziali PEC β†’ user fornisce 2. pec_list_messages(max_results=1) β†’ trova ultima PEC 3. pec_get_message(message_id) β†’ unwrap, trova attachment PDF index 0 4. pec_get_attachment(message_id, attachment_index=0) β†’ scarica PDF base64 5. Chiede credenziali Infocert β†’ user fornisce 6. Upload PDF su storage temporaneo β†’ ottiene URL 7. infocert_sign_document(username, password, pin, pdf_url) β†’ firma PDF, ottiene signed_url 8. Download signed PDF da signed_url 9. pec_send_message( pec_email, pec_password, to=original_sender, subject="Re: " + original_subject, body="In allegato il documento firmato", attachments=[{filename: "documento_firmato.pdf", data: base64}] ) 10. Agent risponde: "βœ… PDF scaricato dalla PEC, firmato digitalmente con Infocert e reinviato via PEC al mittente originale" ``` --- ### Use Case 3: Workflow automatico PEC β†’ Teams **Richiesta utente:** > "Quando arriva una PEC con PDF, caricalo automaticamente su Teams nel canale Contratti" **Agent workflow:** ``` 1. Chiede credenziali PEC 2. pec_list_messages(max_results=20) β†’ filtra solo PEC con has_attachments=true 3. Per ogni PEC: a) pec_get_message(message_id) β†’ unwrap, estrae allegati PDF b) Per ogni PDF: pec_get_attachment(message_id, index) β†’ scarica PDF base64 c) teams_send_message( chat_id="Contratti", message="Nuovo documento PEC ricevuto", attachment=pdf_data ) 4. Agent risponde: "βœ… Caricati 5 PDF da PEC su Teams: - denuncia_sinistro.pdf (da PEC del 04/06) - fattura_12345.pdf (da PEC del 03/06) - contratto_firmato.pdf (da PEC del 02/06)" ``` --- ## πŸ“¦ SBUSTAMENTO PEC (UNWRAPPING) ### Struttura Email PEC Una PEC contiene 3 componenti principali: ``` Email Container (envelope PEC) β”‚ β”œβ”€β”€ postacert.eml ← MESSAGGIO ORIGINALE β”‚ β”œβ”€β”€ headers (From, To, Subject, Date originali) β”‚ β”œβ”€β”€ body (testo/HTML originale) β”‚ └── attachments (allegati originali) β”‚ β”œβ”€β”€ daticert.xml ← METADATI CERTIFICAZIONE β”‚ β”œβ”€β”€ tipo certificazione (consegna/accettazione/errore) β”‚ β”œβ”€β”€ data/ora certificazione β”‚ β”œβ”€β”€ mittente/destinatario β”‚ └── info provider PEC β”‚ └── smime.p7s ← FIRMA DIGITALE └── certificato provider PEC ``` ### Algoritmo Unwrapping ```python def unwrap_pec(raw_email: bytes) -> Dict: # 1. Parse container container_msg = parser.parsebytes(raw_email) # 2. Verifica se Γ¨ PEC is_pec = is_pec_message(container_msg) # Controlla: X-Trasporto: posta-certificata # Cerca: postacert.eml e daticert.xml # 3. Estrai postacert.eml for part in container_msg.walk(): if part.get_filename() == 'postacert.eml': original_msg = parser.parsebytes(part.get_payload(decode=True)) # 4. Estrai allegati da original_msg attachments = [] for part in original_msg.walk(): if part.get_content_disposition() == 'attachment': if filename not in PEC_SYSTEM_FILES: # escludi postacert.eml, daticert.xml, smime.p7s attachments.append({ 'filename': filename, 'data': base64.b64encode(payload) }) # 5. Estrai daticert.xml daticert_xml = extract_daticert(container_msg) metadata = parse_daticert_metadata(daticert_xml) return { 'is_pec': True, 'container': {...}, 'original_message': { 'from': original_msg['From'], 'subject': original_msg['Subject'], 'body_text': get_text_content(original_msg), 'attachments': attachments }, 'daticert': daticert_xml, 'metadata': metadata } ``` ### Tipi di PEC | Tipo | Subject Pattern | Significato | |------|-----------------|-------------| | **delivery** | `CONSEGNA:`, `avvenuta consegna` | Ricevuta di consegna (messaggio consegnato) | | **acceptance** | `ACCETTAZIONE:`, `presa in carico` | Ricevuta di accettazione (messaggio accettato dal provider) | | **error** | `ANOMALIA:`, `mancata consegna`, `errore` | Errore nella consegna | | **normal** | Altro | PEC normale (messaggio originale) | --- ## πŸ”Œ PROVIDER PEC SUPPORTATI ### Auto-detection Provider Il sistema riconosce automaticamente il provider PEC dal dominio email: ```python PEC_PROVIDERS = { 'legalmail': { 'domains': ['@legalmail.it'], 'imap_host': 'mbox.cert.legalmail.it', 'imap_port': 993, 'smtp_host': 'sendm.cert.legalmail.it', 'smtp_port': 465 }, 'aruba': { 'domains': ['@pec.it', '@arubapec.it'], 'imap_host': 'imaps.pec.aruba.it', 'imap_port': 993, 'smtp_host': 'smtps.pec.aruba.it', 'smtp_port': 465 }, 'postecert': { 'domains': ['@postecert.it'], 'imap_host': 'imap.postecert.it', 'imap_port': 993, 'smtp_host': 'smtp.postecert.it', 'smtp_port': 465 } } ``` ### Aggiungere Provider Custom Per provider non riconosciuti, l'agent puΓ² richiedere parametri IMAP/SMTP: ```python config = get_provider_config( email="user@customerpec.it", imap_host="imap.customerpec.it", # Custom smtp_host="smtp.customerpec.it" # Custom ) ``` --- ## πŸ’» IMPLEMENTAZIONE TECNICA ### File Structure ``` iris/ β”œβ”€β”€ src/ β”‚ └── mcp_servers/ β”‚ β”œβ”€β”€ http_server.py # MCP server principale (modificato) β”‚ └── pec/ β”‚ β”œβ”€β”€ __init__.py # Export PECOperations β”‚ β”œβ”€β”€ pec_operations.py # Implementazione tool PEC β”‚ β”œβ”€β”€ pec_unwrapper.py # Sbustamento PEC β”‚ └── pec_providers.py # Config provider IMAP/SMTP └── test_pec_simple.py # Test IMAP connection ``` --- ### PECOperations Class **File:** `src/mcp_servers/pec/pec_operations.py` ```python class PECOperations(BaseOperation): """Operations for PEC (Italian Certified Email) via IMAP/SMTP.""" def __init__(self, graph_client=None): self.graph_client = graph_client self.area_name = "pec" self.unwrapper = PECUnwrapper() def get_supported_actions(self) -> List[str]: return ["list_messages", "get_message", "send_message", "get_attachment"] def _connect_imap(self, pec_email: str, pec_password: str) -> imaplib.IMAP4_SSL: # Auto-detect provider config = get_provider_config(pec_email) # Connect IMAP SSL mail = imaplib.IMAP4_SSL(config['imap_host'], config['imap_port']) mail.login(pec_email, pec_password) return mail def _list_messages(self, params: Dict) -> Dict: mail = self._connect_imap(params['pec_email'], params['pec_password']) mail.select("INBOX") # List messages status, msg_nums = mail.search(None, "ALL") # For each message, check if PEC messages = [] for msg_id in msg_nums[-max_results:]: msg_data = mail.fetch(msg_id, "(BODY.PEEK[HEADER])") msg = email.message_from_bytes(msg_data[0][1]) is_pec = self.unwrapper.is_pec_message(msg) messages.append({ 'id': msg_id, 'subject': msg['Subject'], 'is_pec': is_pec, ... }) return {'messages': messages, 'total': len(msg_nums)} ``` --- ### PECUnwrapper Class **File:** `src/mcp_servers/pec/pec_unwrapper.py` ```python class PECUnwrapper: def is_pec_message(self, message: EmailMessage) -> bool: """Check if message is PEC""" # Check X-Trasporto: posta-certificata if 'posta-certificata' in message.get('X-Trasporto', ''): return True # Check subject patterns subject = message.get('Subject', '').lower() if any(p in subject for p in ['posta certificata:', 'consegna:', 'accettazione:']): return True # Check attachments has_postacert = False has_daticert = False for part in message.walk(): filename = part.get_filename() if filename: if 'postacert.eml' in filename.lower(): has_postacert = True if 'daticert.xml' in filename.lower(): has_daticert = True return has_postacert and has_daticert def unwrap_pec(self, raw_email: bytes) -> Dict: """Full PEC unwrapping""" container_msg = self.parser.parsebytes(raw_email) # Extract postacert.eml original_msg = self.extract_original_message(container_msg) # Extract attachments attachments = self.extract_attachments(original_msg, skip_pec_files=True) # Extract daticert.xml daticert = self.extract_daticert(container_msg) metadata = self._parse_daticert_metadata(daticert) return { 'is_pec': True, 'pec_type': self._detect_pec_type(container_msg), 'container': {...}, 'original_message': { 'from': original_msg['From'], 'attachments': attachments }, 'daticert': daticert, 'metadata': metadata } ``` --- ### Integration in http_server.py **1. Import:** ```python from .pec import PECOperations ``` **2. Inizializzazione:** ```python pec_ops = PECOperations() ``` **3. Registrazione tool in MCP_TOOLS:** ```python MCP_TOOLS = { # ... altri tool ... "pec_list_messages": { "description": "List PEC messages with automatic unwrapping...", "inputSchema": { "type": "object", "properties": { "pec_email": {"type": "string"}, "pec_password": {"type": "string"}, "folder": {"type": "string", "default": "INBOX"}, "max_results": {"type": "integer", "default": 10} }, "required": ["pec_email", "pec_password"] } }, # ... altri 3 tool PEC ... } ``` **4. Handler:** ```python elif tool_name == "pec_list_messages": loop = asyncio.get_event_loop() result = await loop.run_in_executor( None, pec_ops.execute, "list_messages", params ) return result ``` --- ## πŸ§ͺ TESTING ### Test IMAP Connection (Legalmail) **File:** `test_pec_simple.py` ```bash cd /workspace/iris python test_pec_simple.py ``` **Result:** ``` βœ… Connected to mbox.cert.legalmail.it:993 βœ… Login successful for filippo.savarese@legalmail.it βœ… Found 6 messages πŸ“§ Last message: From: posta-certificata@telecompost.it Subject: CONSEGNA: Lettera denuncia sinistro vettura FB94696 Date: Wed, 4 Jun 2025 11:37:33 +0200 βœ… ALL TESTS PASSED - PEC IMAP is working! ``` ### Credenziali Test (Legalmail) ``` Email: filippo.savarese@legalmail.it Password: 85D@k!K| Provider: Legalmail IMAP: mbox.cert.legalmail.it:993 (SSL) SMTP: sendm.cert.legalmail.it:465 (SSL) ``` ### Test End-to-End (Browser TrustySign) 1. Aprire: https://trustypa.brainaihub.tech/trustysign/ 2. Messaggio: "Controlla la mia PEC" 3. Agent chiede credenziali PEC 4. Fornire: filippo.savarese@legalmail.it / 85D@k!K| 5. Agent esegue `pec_list_messages` 6. Agent mostra lista PEC con sbustamento automatico 7. Richiedere dettagli: "Mostrami i dettagli della prima PEC" 8. Agent esegue `pec_get_message` con unwrapping completo 9. Se ha allegati: "Scarica il primo allegato" 10. Agent esegue `pec_get_attachment` --- ## πŸ“ COMPARISON: PEC vs Infocert vs Microsoft | Feature | PEC MCP | Infocert MCP | Microsoft Graph MCP | |---------|---------|--------------|---------------------| | **Architettura** | Internal operations | External SSE server | Internal operations | | **Protocollo** | IMAP/SMTP nativo | MCP over SSE | REST API Graph | | **Credenziali** | Email/password conversazionale | Username/password/PIN conversazionale | OAuth 2.0 | | **Provider** | Auto-detection (Legalmail, Aruba, PosteCert) | Infocert UAT server | Microsoft 365 | | **Unwrapping** | βœ… Automatico (postacert.eml) | ❌ N/A | ❌ N/A | | **File esclusi** | βœ… Esclude file sistema PEC | ❌ N/A | ❌ N/A | | **Metadata** | βœ… daticert.xml parsed | ❌ N/A | ❌ N/A | | **Testing** | βœ… IMAP test passed (6 messages) | ⚠️ Blocked (external UAT 400) | βœ… OAuth working | --- ## βœ… STATUS ### Completato - βœ… PECOperations con IMAP/SMTP - βœ… PECUnwrapper con sbustamento completo - βœ… Provider auto-detection (Legalmail, Aruba, PosteCert) - βœ… 4 tools integrati in http_server.py - βœ… Test IMAP connection successful (Legalmail) - βœ… Commit: `d911c38` - feat: Add PEC MCP integration ### ⚠️ Problema Identificato (2025-11-24) - ❌ `pec_get_message` output troppo grande β†’ supera context limit agent LangGraph - **Causa**: body_text (5KB+) + body_html (50KB+) + daticert.xml (2KB) = 50-100KB total - **Impatto**: Agent LLM non riesce a processare il risultato - **Soluzione**: Implementare `compact=True` mode (vedi sezione sotto) ### Da Testare - ⏳ Test unwrapping con PEC reale (postacert.eml) - ⏳ Test download allegati - ⏳ Test invio PEC via SMTP --- ## πŸ”œ NEXT STEPS ### 1. **FIX OUTPUT SIZE (Priority 1)** ⚠️ **Problema**: `pec_get_message` ritorna 50-100KB output β†’ supera limit agent LangGraph **Soluzione Rapida - Compact Mode**: Modificare `src/mcp_servers/pec/pec_unwrapper.py`: ```python def unwrap_pec(self, raw_email: bytes, include_attachment_data: bool = False, compact: bool = True) -> Dict[str, Any]: # ← DEFAULT compact=True """ Args: compact: Se True, omette body completo e ritorna solo preview (riduce output 98%) """ # ... parsing container ... if compact: # COMPACT: solo preview 300 chars body_text = self._get_text_content(original_msg) result['original_message'] = { 'from': ..., 'subject': ..., 'body_preview': body_text[:300] + '...', 'body_length': len(body_text), 'has_html': len(body_html) > 0, 'attachments': [...] # solo metadata } # NO body_text completo # NO body_html completo # NO daticert XML completo else: # FULL: tutto incluso (per usi specifici) result['original_message'] = { 'body_text': self._get_text_content(original_msg), 'body_html': self._get_html_content(original_msg), ... } ``` **Output size**: - Prima: 100KB (body completo) - Dopo: 2KB (compact mode) βœ… - Riduzione: 98% **Test**: ```bash python test_pec_compact.py ``` ### 2. **Deploy Fix** ```bash cd /opt/iris && ./deploy.sh ``` ### 3. **Test con Agent LangGraph** Verificare che l'agent riesca a processare il risultato compatto. ### 4. **Estensioni Future** - Supporto ricerca PEC (per mittente, data, allegati) - Tool `pec_get_message_body` separato per body completo (se necessario) - Cache credenziali PEC (come OAuth) - Integration con Infocert (PEC β†’ firma β†’ reinvia) --- ## πŸ“š REFERENCES - **PEC Standard**: https://www.agid.gov.it/it/piattaforme/posta-elettronica-certificata-pec - **Legalmail IMAP/SMTP**: mbox.cert.legalmail.it:993 / sendm.cert.legalmail.it:465 - **Python imaplib**: https://docs.python.org/3/library/imaplib.html - **Python email**: https://docs.python.org/3/library/email.html --- **Data ultima modifica**: 2025-11-10 **Autore**: IRIS Team **Versione**: 1.0.0

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