# π 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!** π