/** * Email MCPlet * pool: media-pool | mcpletType: action | visibility: [app] | auth: passkey workflow * * visibility: ['app'] means this tool is NOT exposed to the LLM directly. * It is invoked only through the Host-controlled action-dispatch path * (Dispatch Agent) after Passkey authentication. * enforcement: 'workflow' — one Passkey ceremony per Director cycle (contextId), * not one per individual email send. */ import { MCPletServer } from '../../mcplet-server.js'; const MOCK_SERVICE_URL = process.env.MOCK_SERVICE_URL ?? 'http://localhost:5100'; const server = new MCPletServer('email-mcplet'); server.registerTool({ name: 'send_email', description: '指定した宛先にメールを送信します。承認済みキャンペーン通知や予約リマインダーの送信に使用します。', inputSchema: { type: 'object', properties: { to: { type: 'string', description: '送信先メールアドレス', }, subject: { type: 'string', description: 'メール件名', }, body: { type: 'string', description: 'メール本文', }, customerId: { type: 'string', description: '顧客ID (任意)', }, }, required: ['to', 'subject', 'body'], }, mcpletType: 'action', pool: 'media-pool', visibility: ['app'], auth: { required: 'passkey', enforcement: 'workflow', promptMessage: 'このキャンペーンのメール一括送信を承認してください', }, handler: async (args, authPayload) => { // In production: verify authPayload against FIDO2 server here. // For demo: log and proceed if authPayload present. if (authPayload) { console.error(`[email-mcplet] Auth verified (challenge: ${authPayload['challenge'] ?? 'n/a'})`); } const res = await fetch(`${MOCK_SERVICE_URL}/email/send`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ to: args['to'], subject: args['subject'], body: args['body'], }), }); if (!res.ok) throw new Error(`Email service returned ${res.status}`); return res.json(); }, }); await server.listen();