UNPKG

overmind-mcp

Version:

Orchestrateur universel agents IA multi-modeles via MCP. Inclut le protocole 'Custom-Nickname' pour identifier vos agents avec des surnoms originaux (The Chaos Prophet, Shadow Sniper, etc.), l'isolation mémoire (Private Memory Context) et le support pour

183 lines • 7.98 kB
/** * ═══════════════════════════════════════════════════════════════════════════ * Audit Trail — Outil MCP : Évaluation des facteurs relatifs à la vie privée (EFVP) * (art. 18.1) * ═══════════════════════════════════════════════════════════════════════════ * * Crée ou consulte une EFVP pour un nouveau projet, un nouveau traitement * ou une nouvelle technologie susceptible d'entraîner des risques pour * la vie privée. * * Stocké dans la table `efvp_records` (overmind_core). La table est créée * automatiquement (CREATE TABLE IF NOT EXISTS) au premier appel. */ import { z } from 'zod'; import { randomUUID } from 'crypto'; import { getPool } from 'overmind-postgres-mcp'; // ── Schéma Zod ──────────────────────────────────────────────────────────────── export const auditEfvpSchema = z.object({ action: z .enum(['create', 'list', 'get']) .optional() .default('list') .describe('create: nouvelle EFVP | list: toutes les EFVP | get: une EFVP par project_name'), project_name: z .string() .min(1) .describe("Nom unique du projet/traitement évalué (ex: 'agent_memory_v2'). Requis pour create et get."), description: z .string() .optional() .describe('Description du projet et du traitement concerné.'), data_categories: z .string() .optional() .describe('Catégories de RP concernées (CSV: "prompts,embeddings,user_ids").'), purposes: z .string() .optional() .describe('Finalités du traitement (art. 4).'), recipients: z .string() .optional() .describe('Destinataires internes/externes des RP.'), retention: z .string() .optional() .describe('Durée et modalités de rétention (ex: "30 jours actif + 5 ans archive").'), risks: z .string() .optional() .describe('Risques pour la vie privée (ré-identification, fuite, corrélation...).'), mitigations: z .string() .optional() .describe("Mesures d'atténuation (anonymisation, chiffrement, consentement, etc.)."), }); // ── Helpers ────────────────────────────────────────────────────────────────── function tsNow() { return Date.now(); } async function ensureEfvpTable(pool) { await pool.query(` CREATE TABLE IF NOT EXISTS efvp_records ( id TEXT PRIMARY KEY, project_name TEXT NOT NULL, description TEXT, data_categories TEXT, purposes TEXT, recipients TEXT, retention TEXT, risks TEXT, mitigations TEXT, status TEXT DEFAULT 'draft', created_at BIGINT, updated_at BIGINT ) `); } function formatEfvp(row) { const created = new Date(row.created_at).toISOString().replace('T', ' ').slice(0, 19); return [ `🔍 **${row.project_name}** \`${row.id.slice(0, 8)}\` — _${row.status}_`, ` - Description: ${row.description ?? '—'}`, ` - Catégories: ${row.data_categories ?? '—'}`, ` - Finalités: ${row.purposes ?? '—'}`, ` - Destinataires: ${row.recipients ?? '—'}`, ` - Rétention: ${row.retention ?? '—'}`, ` - Risques: ${row.risks ?? '—'}`, ` - Mesures d\u2019atténuation: ${row.mitigations ?? '—'}`, ` - Créé: ${created}`, ].join('\n'); } // ── Tool ───────────────────────────────────────────────────────────────────── export async function auditEfvp(args) { try { const pool = getPool(); await ensureEfvpTable(pool); const action = args.action ?? 'list'; // ── CREATE ─────────────────────────────────────────────────────────────── if (action === 'create') { const id = randomUUID(); const now = tsNow(); await pool.query(`INSERT INTO efvp_records (id, project_name, description, data_categories, purposes, recipients, retention, risks, mitigations, status, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, 'draft', $10, $11)`, [ id, args.project_name, args.description ?? null, args.data_categories ?? null, args.purposes ?? null, args.recipients ?? null, args.retention ?? null, args.risks ?? null, args.mitigations ?? null, now, now, ]); return { content: [ { type: 'text', text: `✅ **EFVP créée** pour \`${args.project_name}\`\nID: \`${id}\`\nStatut: \`draft\` — À réviser par le responsable de la protection des RP avant publication.`, }, ], }; } // ── LIST ───────────────────────────────────────────────────────────────── if (action === 'list') { const res = await pool.query(`SELECT * FROM efvp_records ORDER BY created_at DESC`); if (res.rows.length === 0) { return { content: [ { type: 'text', text: '📭 Aucune EFVP enregistrée.' }, ], }; } return { content: [ { type: 'text', text: `🔍 **${res.rows.length} EFVP(s)**\n\n${res.rows.map(formatEfvp).join('\n\n')}`, }, ], }; } // ── GET ────────────────────────────────────────────────────────────────── if (action === 'get') { const res = await pool.query(`SELECT * FROM efvp_records WHERE project_name = $1`, [args.project_name]); if (res.rows.length === 0) { return { content: [ { type: 'text', text: `❌ Aucune EFVP pour le projet \`${args.project_name}\`.`, }, ], isError: true, }; } return { content: [{ type: 'text', text: formatEfvp(res.rows[0]) }], }; } // unreachable return { content: [{ type: 'text', text: `❌ Action inconnue: ${action}` }], isError: true, }; } catch (err) { return { content: [ { type: 'text', text: `❌ Erreur audit_efvp: ${err instanceof Error ? err.message : String(err)}`, }, ], isError: true, }; } } //# sourceMappingURL=audit_efvp.js.map