(server: McpServer, client: McpClient)
| 18 | } |
| 19 | |
| 20 | export function registerMessageTools(server: McpServer, client: McpClient): void { |
| 21 | server.registerTool( |
| 22 | "send_message", |
| 23 | { |
| 24 | title: "Send email", |
| 25 | annotations: { destructiveHint: false }, |
| 26 | description: |
| 27 | "Use when starting a NEW email thread to a fresh recipient. To respond to a message you can see in `list_messages`, use `reply_to_message` instead — it preserves the In-Reply-To / References headers so the reply lands in the same thread, which this tool deliberately does not do. Attach files via `attachments`; pass base64 strings produced by other tools (e.g. `get_attachment`) verbatim — don't hand-encode raw text. **`pending_review` is not failure.** If the agent's review policy holds the send, the response is `{ status: \"pending_review\", message_id: ... }`; the message is held for human review — do not retry. Check on it with `list_messages` (held drafts show review_status=pending_review) / `get_message`.", |
| 28 | inputSchema: strictInputSchema({ |
| 29 | to: z.array(z.string()).describe("Recipient email addresses (one or more)."), |
| 30 | subject: z.string(), |
| 31 | body: z.string().describe("Plain-text body. Use `html_body` for HTML."), |
| 32 | html_body: z.string().optional(), |
| 33 | cc: z.array(z.string()).optional(), |
| 34 | bcc: z.array(z.string()).optional(), |
| 35 | attachments: attachmentsArraySchema, |
| 36 | conversation_id: z |
| 37 | .string() |
| 38 | .optional() |
| 39 | .describe("Optional conversation grouping ID. Server generates one if omitted."), |
| 40 | idempotency_key: z |
| 41 | .string() |
| 42 | .optional() |
| 43 | .describe( |
| 44 | "Stable key for retry-safe sends. Set to deduplicate when the caller has its own retry loop (e.g. a stable triggering event id). When omitted the SDK mints a fresh UUIDv4 per call — protects against network-layer retries only, not user-driven retries.", |
| 45 | ), |
| 46 | email: z |
| 47 | .string() |
| 48 | .optional() |
| 49 | .describe( |
| 50 | "Sending agent's inbox. Omit to use the credential's bound agent (agent-scoped credentials).", |
| 51 | ), |
| 52 | }), |
| 53 | }, |
| 54 | async (args) => |
| 55 | runTool(() => { |
| 56 | const opts: SendOpts = |
| 57 | args.idempotency_key !== undefined |
| 58 | ? { idempotencyKey: args.idempotency_key } |
| 59 | : {}; |
| 60 | return client.send( |
| 61 | { |
| 62 | to: args.to, |
| 63 | subject: args.subject, |
| 64 | body: args.body, |
| 65 | ...(args.html_body !== undefined ? { htmlBody: args.html_body } : {}), |
| 66 | ...(args.cc !== undefined ? { cc: args.cc } : {}), |
| 67 | ...(args.bcc !== undefined ? { bcc: args.bcc } : {}), |
| 68 | ...(mapAttachments(args.attachments) !== undefined |
| 69 | ? { attachments: mapAttachments(args.attachments) } |
| 70 | : {}), |
| 71 | ...(args.conversation_id !== undefined |
| 72 | ? { conversationId: args.conversation_id } |
| 73 | : {}), |
| 74 | }, |
| 75 | opts, |
| 76 | args.email, |
| 77 | ); |
no test coverage detected