create_email_message_by_user_id
Create and send personalized email messages to users with dynamic tags for subject and body, using the user's ID.
Instructions
Create and send an email message to a user
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| user_id | Yes | ID of the email | |
| from | Yes | From field of the email. The default is the educator reply to email. It is possible to use tags delimited by two pairs of curly braces, i.e. `{{educator.reply_to}}`. | |
| subject | Yes | Subject line of the email. It is possible to use tags delimited by two pairs of curly braces, i.e. `{{user.full_name}}`. | |
| body | Yes | Body field of the email, allowing HTML format. It is possible to use tags delimited by two pairs of curly braces, i.e. `{{user.full_name}}`. Since this is JSON it does not accept `"` characters and multi-line strings. The body must be properly escaped before sending. This can be done automatically or manually. Example tool: https://www.freeformatter.com/json-escape.html#before-output |
Implementation Reference
- src/tools/emails.ts:32-42 (handler)The async handler function that executes the tool logic: destructures user_id from the rest of the body, calls apiPost to POST /users/{user_id}/emails, logs the response, and returns formatted output.
async ({ user_id, ...body }) => { try { const record = await apiPost<EduframeRecord>(`/users/${user_id}/emails`, body); void logResponse("create_email_message_by_user_id", { user_id, ...body }, record); return formatShow(record, "email"); } catch (error) { return formatError(error); } }, ); } - src/tools/emails.ts:10-31 (schema)Input schema and tool metadata: defines 'description', 'annotations', and 'inputSchema' with Zod validators for user_id (number), from (string), subject (string), and body (string).
{ description: "Create and send an email message to a user", annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }, inputSchema: { user_id: z.number().int().positive().describe("ID of the email"), from: z .string() .describe( "From field of the email. The default is the educator reply to email. It is possible to use tags delimited by two pairs of curly braces, i.e. `{{educator.reply_to}}`.", ), subject: z .string() .describe( "Subject line of the email. It is possible to use tags delimited by two pairs of curly braces, i.e. `{{user.full_name}}`.", ), body: z .string() .describe( 'Body field of the email, allowing HTML format. It is possible to use tags delimited by two pairs of curly braces, i.e. `{{user.full_name}}`. Since this is JSON it does not accept `"` characters and multi-line strings. The body must be properly escaped before sending. This can be done automatically or manually. Example tool: https://www.freeformatter.com/json-escape.html#before-output', ), }, }, - src/tools/emails.ts:7-42 (registration)The registerEmailTools function registers the tool on the McpServer via server.registerTool('create_email_message_by_user_id', ...).
export function registerEmailTools(server: McpServer): void { server.registerTool( "create_email_message_by_user_id", { description: "Create and send an email message to a user", annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }, inputSchema: { user_id: z.number().int().positive().describe("ID of the email"), from: z .string() .describe( "From field of the email. The default is the educator reply to email. It is possible to use tags delimited by two pairs of curly braces, i.e. `{{educator.reply_to}}`.", ), subject: z .string() .describe( "Subject line of the email. It is possible to use tags delimited by two pairs of curly braces, i.e. `{{user.full_name}}`.", ), body: z .string() .describe( 'Body field of the email, allowing HTML format. It is possible to use tags delimited by two pairs of curly braces, i.e. `{{user.full_name}}`. Since this is JSON it does not accept `"` characters and multi-line strings. The body must be properly escaped before sending. This can be done automatically or manually. Example tool: https://www.freeformatter.com/json-escape.html#before-output', ), }, }, async ({ user_id, ...body }) => { try { const record = await apiPost<EduframeRecord>(`/users/${user_id}/emails`, body); void logResponse("create_email_message_by_user_id", { user_id, ...body }, record); return formatShow(record, "email"); } catch (error) { return formatError(error); } }, ); } - src/tools/index.ts:128-132 (registration)The registerAllTools function iterates over all tool registrations including registerEmailTools, which is the entry point that causes the tool to be registered on the server.
export function registerAllTools(server: McpServer): void { for (const register of tools) { register(server); } } - src/api.ts:163-174 (helper)The apiPost helper function that performs the actual HTTP POST request to the Eduframe API, used by the handler to POST to /users/{user_id}/emails.
export async function apiPost<T>(path: string, body: unknown): Promise<T> { const { token } = getConfig(); const url = buildUrl(path); const response = await fetch(url.toString(), { method: "POST", headers: buildHeaders(token), body: JSON.stringify(body), }); return handleResponse<T>(response); }