diff --git a/lib/emails/__tests__/getEmailFooter.test.ts b/lib/emails/__tests__/getEmailFooter.test.ts
index 0e5890a9..b009b713 100644
--- a/lib/emails/__tests__/getEmailFooter.test.ts
+++ b/lib/emails/__tests__/getEmailFooter.test.ts
@@ -38,4 +38,20 @@ describe("getEmailFooter", () => {
expect(footer).toContain("font-size:12px");
expect(footer).toContain("color:#6b7280");
});
+
+ it("includes artist workspace when artistName is provided", () => {
+ const footer = getEmailFooter("room-id", "Taylor Swift");
+ expect(footer).toContain("From Taylor Swift's workspace");
+ });
+
+ it("excludes artist line when artistName is not provided", () => {
+ const footer = getEmailFooter("room-id");
+ expect(footer).not.toContain("workspace");
+ });
+
+ it("includes artist workspace without roomId", () => {
+ const footer = getEmailFooter(undefined, "Drake");
+ expect(footer).toContain("From Drake's workspace");
+ expect(footer).not.toContain("chat.recoupable.com");
+ });
});
diff --git a/lib/emails/getEmailFooter.ts b/lib/emails/getEmailFooter.ts
index 2557ef35..6fe173d0 100644
--- a/lib/emails/getEmailFooter.ts
+++ b/lib/emails/getEmailFooter.ts
@@ -2,9 +2,17 @@
* Generates a standardized email footer HTML.
*
* @param roomId - Optional room ID for the chat link. If not provided, only the reply note is shown.
+ * @param artistName - Optional artist name to display in the footer.
* @returns HTML string for the email footer.
*/
-export function getEmailFooter(roomId?: string): string {
+export function getEmailFooter(roomId?: string, artistName?: string): string {
+ const artistLine = artistName
+ ? `
+
+ From ${artistName}'s workspace
+
`.trim()
+ : "";
+
const replyNote = `
Note: you can reply directly to this email to continue the conversation.
@@ -22,6 +30,7 @@ export function getEmailFooter(roomId?: string): string {
return `
+${artistLine}
${replyNote}
${chatLink}`.trim();
}
diff --git a/lib/emails/inbound/generateEmailResponse.ts b/lib/emails/inbound/generateEmailResponse.ts
index 95f19c43..59edf811 100644
--- a/lib/emails/inbound/generateEmailResponse.ts
+++ b/lib/emails/inbound/generateEmailResponse.ts
@@ -3,6 +3,7 @@ import { ChatRequestBody } from "@/lib/chat/validateChatRequest";
import getGeneralAgent from "@/lib/agents/generalAgent/getGeneralAgent";
import { getEmailRoomMessages } from "@/lib/emails/inbound/getEmailRoomMessages";
import { getEmailFooter } from "@/lib/emails/getEmailFooter";
+import { selectRoomWithArtist } from "@/lib/supabase/rooms/selectRoomWithArtist";
/**
* Generates the assistant response HTML for an email, including:
@@ -29,8 +30,9 @@ export async function generateEmailResponse(
const chatResponse = await agent.generate({ messages });
const text = chatResponse.text;
+ const roomData = await selectRoomWithArtist(roomId);
const bodyHtml = marked(text);
- const footerHtml = getEmailFooter(roomId);
+ const footerHtml = getEmailFooter(roomId, roomData?.artist_name || undefined);
const html = `${bodyHtml}\n\n${footerHtml}`;
return { text, html };
diff --git a/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts b/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts
index 509e1aba..84c5344d 100644
--- a/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts
+++ b/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts
@@ -4,11 +4,16 @@ import { registerSendEmailTool } from "../registerSendEmailTool";
import { NextResponse } from "next/server";
const mockSendEmailWithResend = vi.fn();
+const mockSelectRoomWithArtist = vi.fn();
vi.mock("@/lib/emails/sendEmail", () => ({
sendEmailWithResend: (...args: unknown[]) => mockSendEmailWithResend(...args),
}));
+vi.mock("@/lib/supabase/rooms/selectRoomWithArtist", () => ({
+ selectRoomWithArtist: (...args: unknown[]) => mockSelectRoomWithArtist(...args),
+}));
+
describe("registerSendEmailTool", () => {
let mockServer: McpServer;
let registeredHandler: (args: unknown) => Promise;
diff --git a/lib/mcp/tools/registerSendEmailTool.ts b/lib/mcp/tools/registerSendEmailTool.ts
index 3f29eb5d..93d6565a 100644
--- a/lib/mcp/tools/registerSendEmailTool.ts
+++ b/lib/mcp/tools/registerSendEmailTool.ts
@@ -5,6 +5,7 @@ import { getToolResultSuccess } from "@/lib/mcp/getToolResultSuccess";
import { getToolResultError } from "@/lib/mcp/getToolResultError";
import { RECOUP_FROM_EMAIL } from "@/lib/const";
import { getEmailFooter } from "@/lib/emails/getEmailFooter";
+import { selectRoomWithArtist } from "@/lib/supabase/rooms/selectRoomWithArtist";
import { NextResponse } from "next/server";
import { marked } from "marked";
@@ -24,7 +25,8 @@ export function registerSendEmailTool(server: McpServer): void {
async (args: SendEmailInput) => {
const { to, cc = [], subject, text, html = "", headers = {}, room_id } = args;
- const footer = getEmailFooter(room_id);
+ const roomData = room_id ? await selectRoomWithArtist(room_id) : null;
+ const footer = getEmailFooter(room_id, roomData?.artist_name || undefined);
const bodyHtml = html || (text ? marked(text) : "");
const htmlWithFooter = `${bodyHtml}\n\n${footer}`;
diff --git a/lib/supabase/rooms/selectRoomWithArtist.ts b/lib/supabase/rooms/selectRoomWithArtist.ts
new file mode 100644
index 00000000..32f89288
--- /dev/null
+++ b/lib/supabase/rooms/selectRoomWithArtist.ts
@@ -0,0 +1,31 @@
+import supabase from "../serverClient";
+
+type RoomWithArtist = {
+ id: string;
+ artist_id: string | null;
+ artist_name: string | null;
+};
+
+/**
+ * Select a room with its associated artist name from accounts table.
+ *
+ * @param roomId - The room ID to query.
+ * @returns The room data with artist name, or null if not found.
+ */
+export async function selectRoomWithArtist(roomId: string): Promise {
+ const { data, error } = await supabase
+ .from("rooms")
+ .select("id, artist_id, accounts!rooms_artist_id_fkey(name)")
+ .eq("id", roomId)
+ .single();
+
+ if (error || !data) return null;
+
+ const account = Array.isArray(data.accounts) ? data.accounts[0] : data.accounts;
+
+ return {
+ id: data.id,
+ artist_id: data.artist_id,
+ artist_name: account?.name || null,
+ };
+}